Refresh ops tooling around current agent lanes (#142)

Co-authored-by: Codex Agent <codex@hermes.local>
Co-committed-by: Codex Agent <codex@hermes.local>
This commit was merged in pull request #142.
This commit is contained in:
2026-04-04 22:43:48 +00:00
committed by Timmy Time
parent 21661b0d6e
commit 2bf79c2286
3 changed files with 584 additions and 516 deletions

View File

@@ -1,300 +1,224 @@
#!/usr/bin/env bash
# ── Consolidated Ops Panel ─────────────────────────────────────────────
# Everything in one view. Designed for a half-screen pane (~100x45).
# ── Workflow Ops Panel ─────────────────────────────────────────────────
# Current-state dashboard for review, dispatch, and freshness.
# This intentionally reflects the post-loop, Hermes-sidecar workflow.
# ───────────────────────────────────────────────────────────────────────
B='\033[1m' ; D='\033[2m' ; R='\033[0m' ; U='\033[4m'
G='\033[32m' ; Y='\033[33m' ; RD='\033[31m' ; C='\033[36m' ; M='\033[35m' ; W='\033[37m'
OK="${G}${R}" ; WARN="${Y}${R}" ; FAIL="${RD}${R}" ; OFF="${D}${R}"
set -euo pipefail
TOKEN=$(cat ~/.hermes/gitea_token_vps 2>/dev/null)
API="http://143.198.27.163:3000/api/v1/repos/rockachopa/Timmy-time-dashboard"
B='\033[1m'
D='\033[2m'
R='\033[0m'
U='\033[4m'
G='\033[32m'
Y='\033[33m'
RD='\033[31m'
M='\033[35m'
OK="${G}${R}"
WARN="${Y}${R}"
FAIL="${RD}${R}"
resolve_gitea_url() {
if [ -n "${GITEA_URL:-}" ]; then
printf '%s\n' "${GITEA_URL%/}"
return 0
fi
if [ -f "$HOME/.hermes/gitea_api" ]; then
python3 - "$HOME/.hermes/gitea_api" <<'PY'
from pathlib import Path
import sys
raw = Path(sys.argv[1]).read_text().strip().rstrip("/")
print(raw[:-7] if raw.endswith("/api/v1") else raw)
PY
return 0
fi
if [ -f "$HOME/.config/gitea/base-url" ]; then
tr -d '[:space:]' < "$HOME/.config/gitea/base-url"
return 0
fi
echo "ERROR: set GITEA_URL or create ~/.hermes/gitea_api" >&2
return 1
}
resolve_ops_token() {
local token_file
for token_file in \
"$HOME/.config/gitea/timmy-token" \
"$HOME/.hermes/gitea_token_vps" \
"$HOME/.hermes/gitea_token_timmy"; do
if [ -f "$token_file" ]; then
tr -d '[:space:]' < "$token_file"
return 0
fi
done
return 1
}
GITEA_URL="$(resolve_gitea_url)"
CORE_REPOS="${CORE_REPOS:-Timmy_Foundation/the-nexus Timmy_Foundation/timmy-home Timmy_Foundation/timmy-config Timmy_Foundation/hermes-agent}"
TOKEN="$(resolve_ops_token || true)"
[ -z "$TOKEN" ] && echo "WARN: no approved Timmy Gitea token found; panel will use unauthenticated API calls" >&2
# ── HEADER ─────────────────────────────────────────────────────────────
echo ""
echo -e " ${B}${M}HERMES OPERATIONS${R} ${D}$(date '+%a %b %d %H:%M:%S')${R}"
echo -e " ${B}${M}WORKFLOW OPERATIONS${R} ${D}$(date '+%a %b %d %H:%M:%S')${R}"
echo -e " ${D}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${R}"
echo ""
# ── SERVICES ───────────────────────────────────────────────────────────
echo -e " ${B}${U}SERVICES${R}"
echo ""
# Gateway
GW_PID=$(pgrep -f "hermes.*gateway.*run" 2>/dev/null | head -1)
[ -n "$GW_PID" ] && echo -e " ${OK} Gateway ${D}pid $GW_PID${R}" \
|| echo -e " ${FAIL} Gateway ${RD}DOWN — run: hermes gateway start${R}"
# Kimi Code loop
KIMI_PID=$(pgrep -f "kimi-loop.sh" 2>/dev/null | head -1)
[ -n "$KIMI_PID" ] && echo -e " ${OK} Kimi Loop ${D}pid $KIMI_PID${R}" \
|| echo -e " ${FAIL} Kimi Loop ${RD}DOWN — run: ops-wake-kimi${R}"
# Active Kimi Code worker
KIMI_WORK=$(pgrep -f "kimi.*--print" 2>/dev/null | head -1)
if [ -n "$KIMI_WORK" ]; then
echo -e " ${OK} Kimi Code ${D}pid $KIMI_WORK ${G}working${R}"
elif [ -n "$KIMI_PID" ]; then
echo -e " ${WARN} Kimi Code ${Y}between issues${R}"
GW_PID=$(pgrep -f "hermes.*gateway.*run" 2>/dev/null | head -1 || true)
if [ -n "${GW_PID:-}" ]; then
echo -e " ${OK} Hermes Gateway ${D}pid $GW_PID${R}"
else
echo -e " ${OFF} Kimi Code ${D}not running${R}"
echo -e " ${FAIL} Hermes Gateway ${RD}down${R}"
fi
# Claude Code loop (parallel workers)
CLAUDE_PID=$(pgrep -f "claude-loop.sh" 2>/dev/null | head -1)
CLAUDE_WORKERS=$(pgrep -f "claude.*--print.*--dangerously" 2>/dev/null | wc -l | tr -d ' ')
if [ -n "$CLAUDE_PID" ]; then
echo -e " ${OK} Claude Loop ${D}pid $CLAUDE_PID ${G}${CLAUDE_WORKERS} workers active${R}"
if curl -s --max-time 3 "$GITEA_URL/api/v1/version" >/dev/null 2>&1; then
echo -e " ${OK} Gitea ${D}${GITEA_URL}${R}"
else
echo -e " ${FAIL} Claude Loop ${RD}DOWN — run: ops-wake-claude${R}"
echo -e " ${FAIL} Gitea ${RD}unreachable${R}"
fi
# Gemini Code loop
GEMINI_PID=$(pgrep -f "gemini-loop.sh" 2>/dev/null | head -1)
GEMINI_WORK=$(pgrep -f "gemini.*--print" 2>/dev/null | head -1)
if [ -n "$GEMINI_PID" ]; then
if [ -n "$GEMINI_WORK" ]; then
echo -e " ${OK} Gemini Loop ${D}pid $GEMINI_PID ${G}working${R}"
else
echo -e " ${WARN} Gemini Loop ${D}pid $GEMINI_PID ${Y}between issues${R}"
fi
if hermes cron list >/dev/null 2>&1; then
echo -e " ${OK} Hermes Cron ${D}reachable${R}"
else
echo -e " ${FAIL} Gemini Loop ${RD}DOWN — run: ops-wake-gemini${R}"
echo -e " ${WARN} Hermes Cron ${Y}not responding${R}"
fi
# Timmy Orchestrator
TIMMY_PID=$(pgrep -f "timmy-orchestrator.sh" 2>/dev/null | head -1)
if [ -n "$TIMMY_PID" ]; then
TIMMY_LAST=$(tail -1 "$HOME/.hermes/logs/timmy-orchestrator.log" 2>/dev/null | sed 's/.*TIMMY: //')
echo -e " ${OK} Timmy (Ollama) ${D}pid $TIMMY_PID ${G}${TIMMY_LAST:0:30}${R}"
FRESHNESS_OUTPUT=$("$HOME/.hermes/bin/pipeline-freshness.sh" 2>/dev/null || true)
FRESHNESS_STATUS=$(printf '%s\n' "$FRESHNESS_OUTPUT" | awk -F= '/^status=/{print $2}')
FRESHNESS_REASON=$(printf '%s\n' "$FRESHNESS_OUTPUT" | awk -F= '/^reason=/{print $2}')
if [ "$FRESHNESS_STATUS" = "ok" ]; then
echo -e " ${OK} Export Freshness ${D}${FRESHNESS_REASON:-within freshness window}${R}"
elif [ -n "$FRESHNESS_STATUS" ]; then
echo -e " ${WARN} Export Freshness ${Y}${FRESHNESS_REASON:-lagging}${R}"
else
echo -e " ${FAIL} Timmy ${RD}DOWN — run: ops-wake-timmy${R}"
fi
# Gitea VPS
if curl -s --max-time 3 "http://143.198.27.163:3000/api/v1/version" >/dev/null 2>&1; then
echo -e " ${OK} Gitea VPS ${D}143.198.27.163:3000${R}"
else
echo -e " ${FAIL} Gitea VPS ${RD}unreachable${R}"
fi
# Matrix staging
HTTP=$(curl -s --max-time 3 -o /dev/null -w "%{http_code}" "http://143.198.27.163/")
[ "$HTTP" = "200" ] && echo -e " ${OK} Matrix Staging ${D}143.198.27.163${R}" \
|| echo -e " ${FAIL} Matrix Staging ${RD}HTTP $HTTP${R}"
# Dev cycle cron
CRON_LINE=$(hermes cron list 2>&1 | grep -B1 "consolidated-dev-cycle" | head -1 2>/dev/null)
if echo "$CRON_LINE" | grep -q "active"; then
NEXT=$(hermes cron list 2>&1 | grep -A4 "consolidated-dev-cycle" | grep "Next" | awk '{print $NF}' | cut -dT -f2 | cut -d. -f1)
echo -e " ${OK} Dev Cycle ${D}every 30m, next ${NEXT:-?}${R}"
else
echo -e " ${FAIL} Dev Cycle Cron ${RD}MISSING${R}"
echo -e " ${WARN} Export Freshness ${Y}unknown${R}"
fi
echo ""
# ── KIMI STATS ─────────────────────────────────────────────────────────
echo -e " ${B}${U}KIMI${R}"
echo ""
KIMI_LOG="$HOME/.hermes/logs/kimi-loop.log"
if [ -f "$KIMI_LOG" ]; then
COMPLETED=$(grep -c "SUCCESS:" "$KIMI_LOG" 2>/dev/null | tail -1 || echo 0)
FAILED=$(grep -c "FAILED:" "$KIMI_LOG" 2>/dev/null | tail -1 || echo 0)
LAST_ISSUE=$(grep "=== ISSUE" "$KIMI_LOG" | tail -1 | sed 's/.*=== //' | sed 's/ ===//')
LAST_TIME=$(grep "=== ISSUE\|SUCCESS\|FAILED" "$KIMI_LOG" | tail -1 | cut -d']' -f1 | tr -d '[')
RATE=""
if [ "$COMPLETED" -gt 0 ] && [ "$FAILED" -gt 0 ]; then
TOTAL=$((COMPLETED + FAILED))
PCT=$((COMPLETED * 100 / TOTAL))
RATE=" (${PCT}% success)"
fi
echo -e " Completed ${G}${B}$COMPLETED${R} Failed ${RD}$FAILED${R}${D}$RATE${R}"
echo -e " Current ${C}$LAST_ISSUE${R}"
echo -e " Last seen ${D}$LAST_TIME${R}"
fi
echo ""
# ── CLAUDE STATS ──────────────────────────────────────────────────
echo -e " ${B}${U}CLAUDE${R}"
echo ""
CLAUDE_LOG="$HOME/.hermes/logs/claude-loop.log"
if [ -f "$CLAUDE_LOG" ]; then
CL_COMPLETED=$(grep -c "SUCCESS" "$CLAUDE_LOG" 2>/dev/null | tail -1 || echo 0)
CL_FAILED=$(grep -c "FAILED" "$CLAUDE_LOG" 2>/dev/null | tail -1 || echo 0)
CL_RATE_LIM=$(grep -c "RATE LIMITED" "$CLAUDE_LOG" 2>/dev/null | tail -1 || echo 0)
CL_RATE=""
if [ "$CL_COMPLETED" -gt 0 ] || [ "$CL_FAILED" -gt 0 ]; then
CL_TOTAL=$((CL_COMPLETED + CL_FAILED))
[ "$CL_TOTAL" -gt 0 ] && CL_PCT=$((CL_COMPLETED * 100 / CL_TOTAL)) && CL_RATE=" (${CL_PCT}%)"
fi
echo -e " ${G}${B}$CL_COMPLETED${R} done ${RD}$CL_FAILED${R} fail ${Y}$CL_RATE_LIM${R} rate-limited${D}$CL_RATE${R}"
# Show active workers
ACTIVE="$HOME/.hermes/logs/claude-active.json"
if [ -f "$ACTIVE" ]; then
python3 -c "
python3 - "$GITEA_URL" "$TOKEN" "$CORE_REPOS" <<'PY'
import json
try:
with open('$ACTIVE') as f: active = json.load(f)
for wid, info in sorted(active.items()):
iss = info.get('issue','')
repo = info.get('repo','').split('/')[-1] if info.get('repo') else ''
st = info.get('status','')
if st == 'working':
print(f' \033[36mW{wid}\033[0m \033[33m#{iss}\033[0m \033[2m{repo}\033[0m')
elif st == 'idle':
print(f' \033[2mW{wid} idle\033[0m')
except: pass
" 2>/dev/null
fi
else
echo -e " ${D}(no log yet — start with ops-wake-claude)${R}"
fi
echo ""
import sys
import urllib.error
import urllib.request
from datetime import datetime, timedelta, timezone
# ── GEMINI STATS ─────────────────────────────────────────────────────
echo -e " ${B}${U}GEMINI${R}"
echo ""
GEMINI_LOG="$HOME/.hermes/logs/gemini-loop.log"
if [ -f "$GEMINI_LOG" ]; then
GM_COMPLETED=$(grep -c "SUCCESS:" "$GEMINI_LOG" 2>/dev/null | tail -1 || echo 0)
GM_FAILED=$(grep -c "FAILED:" "$GEMINI_LOG" 2>/dev/null | tail -1 || echo 0)
GM_RATE=""
if [ "$GM_COMPLETED" -gt 0 ] || [ "$GM_FAILED" -gt 0 ]; then
GM_TOTAL=$((GM_COMPLETED + GM_FAILED))
[ "$GM_TOTAL" -gt 0 ] && GM_PCT=$((GM_COMPLETED * 100 / GM_TOTAL)) && GM_RATE=" (${GM_PCT}%)"
fi
GM_LAST=$(grep "=== ISSUE" "$GEMINI_LOG" | tail -1 | sed 's/.*=== //' | sed 's/ ===//')
echo -e " ${G}${B}$GM_COMPLETED${R} done ${RD}$GM_FAILED${R} fail${D}$GM_RATE${R}"
[ -n "$GM_LAST" ] && echo -e " Current ${C}$GM_LAST${R}"
else
echo -e " ${D}(no log yet — start with ops-wake-gemini)${R}"
fi
echo ""
base = sys.argv[1].rstrip("/")
token = sys.argv[2]
repos = sys.argv[3].split()
headers = {"Authorization": f"token {token}"} if token else {}
# ── OPEN PRS ───────────────────────────────────────────────────────────
echo -e " ${B}${U}PULL REQUESTS${R}"
echo ""
curl -s --max-time 5 -H "Authorization: token $TOKEN" "$API/pulls?state=open&limit=8" 2>/dev/null | python3 -c "
import json,sys
try:
prs = json.loads(sys.stdin.read())
if not prs: print(' \033[2m(none open)\033[0m')
for p in prs[:6]:
n = p['number']
t = p['title'][:55]
u = p['user']['login']
print(f' \033[33m#{n:<4d}\033[0m \033[2m{u:8s}\033[0m {t}')
if len(prs) > 6: print(f' \033[2m... +{len(prs)-6} more\033[0m')
except: print(' \033[31m(error fetching)\033[0m')
" 2>/dev/null
echo ""
# ── RECENTLY MERGED ────────────────────────────────────────────────────
echo -e " ${B}${U}RECENTLY MERGED${R}"
echo ""
curl -s --max-time 5 -H "Authorization: token $TOKEN" "$API/pulls?state=closed&sort=updated&limit=5" 2>/dev/null | python3 -c "
import json,sys
try:
prs = json.loads(sys.stdin.read())
merged = [p for p in prs if p.get('merged')][:5]
if not merged: print(' \033[2m(none recent)\033[0m')
for p in merged:
n = p['number']
t = p['title'][:50]
when = p['merged_at'][11:16]
print(f' \033[32m✓ #{n:<4d}\033[0m {t} \033[2m{when}\033[0m')
except: print(' \033[31m(error)\033[0m')
" 2>/dev/null
echo ""
def fetch(path):
req = urllib.request.Request(f"{base}{path}", headers=headers)
with urllib.request.urlopen(req, timeout=5) as resp:
return json.loads(resp.read().decode())
# ── KIMI QUEUE ─────────────────────────────────────────────────────────
echo -e " ${B}${U}KIMI QUEUE${R}"
echo ""
curl -s --max-time 5 -H "Authorization: token $TOKEN" "$API/issues?state=open&limit=50&type=issues" 2>/dev/null | python3 -c "
import json,sys
try:
all_issues = json.loads(sys.stdin.read())
issues = [i for i in all_issues if 'kimi' in [a.get('login','') for a in (i.get('assignees') or [])]]
if not issues: print(' \033[33m⚠ Queue empty — assign more issues to kimi\033[0m')
for i in issues[:6]:
n = i['number']
t = i['title'][:55]
print(f' #{n:<4d} {t}')
if len(issues) > 6: print(f' \033[2m... +{len(issues)-6} more\033[0m')
except: print(' \033[31m(error)\033[0m')
" 2>/dev/null
echo ""
# ── CLAUDE QUEUE ──────────────────────────────────────────────────
echo -e " ${B}${U}CLAUDE QUEUE${R}"
echo ""
# Claude works across multiple repos
python3 -c "
import json, sys, urllib.request
token = '$(cat ~/.hermes/claude_token 2>/dev/null)'
base = 'http://143.198.27.163:3000'
repos = ['rockachopa/Timmy-time-dashboard','rockachopa/alexanderwhitestone.com','replit/timmy-tower','replit/token-gated-economy','rockachopa/hermes-agent']
all_issues = []
def short(repo):
return repo.split("/", 1)[1]
issues = []
pulls = []
review_queue = []
errors = []
for repo in repos:
url = f'{base}/api/v1/repos/{repo}/issues?state=open&limit=50&type=issues'
try:
req = urllib.request.Request(url, headers={'Authorization': f'token {token}'})
resp = urllib.request.urlopen(req, timeout=5)
raw = json.loads(resp.read())
issues = [i for i in raw if 'claude' in [a.get('login','') for a in (i.get('assignees') or [])]]
for i in issues:
i['_repo'] = repo.split('/')[1]
all_issues.extend(issues)
except: continue
if not all_issues:
print(' \033[33m\u26a0 Queue empty \u2014 assign issues to claude\033[0m')
repo_pulls = fetch(f"/api/v1/repos/{repo}/pulls?state=open&limit=20")
for pr in repo_pulls:
pr["_repo"] = repo
pulls.append(pr)
repo_issues = fetch(f"/api/v1/repos/{repo}/issues?state=open&limit=50&type=issues")
for issue in repo_issues:
issue["_repo"] = repo
issues.append(issue)
repo_pull_issues = fetch(f"/api/v1/repos/{repo}/issues?state=open&limit=50&type=pulls")
for item in repo_pull_issues:
assignees = [a.get("login", "") for a in (item.get("assignees") or [])]
if any(name in assignees for name in ("Timmy", "allegro")):
item["_repo"] = repo
review_queue.append(item)
except urllib.error.URLError as exc:
errors.append(f"{repo}: {exc.reason}")
except Exception as exc: # pragma: no cover - defensive panel path
errors.append(f"{repo}: {exc}")
print(" \033[1m\033[4mREVIEW QUEUE\033[0m\n")
if not review_queue:
print(" \033[2m(clear)\033[0m\n")
else:
for i in all_issues[:6]:
n = i['number']
t = i['title'][:45]
r = i['_repo'][:12]
print(f' #{n:<4d} \033[2m{r:12s}\033[0m {t}')
if len(all_issues) > 6:
print(f' \033[2m... +{len(all_issues)-6} more\033[0m')
" 2>/dev/null
for item in review_queue[:8]:
names = ",".join(a.get("login", "") for a in (item.get("assignees") or []))
print(f" #{item['number']:<4d} {short(item['_repo']):12s} {names[:20]:20s} {item['title'][:44]}")
print()
print(" \033[1m\033[4mOPEN PRS\033[0m\n")
if not pulls:
print(" \033[2m(none open)\033[0m\n")
else:
for pr in pulls[:8]:
print(f" #{pr['number']:<4d} {short(pr['_repo']):12s} {pr['user']['login'][:12]:12s} {pr['title'][:48]}")
print()
print(" \033[1m\033[4mDISPATCH QUEUES\033[0m\n")
queue_agents = [
("allegro", "dispatch"),
("codex-agent", "cleanup"),
("groq", "fast ship"),
("claude", "refactor"),
("ezra", "archive"),
("perplexity", "research"),
("KimiClaw", "digest"),
]
for agent, label in queue_agents:
assigned = [
issue
for issue in issues
if agent in [a.get("login", "") for a in (issue.get("assignees") or [])]
]
print(f" {agent:12s} {len(assigned):2d} \033[2m{label}\033[0m")
print()
unassigned = [issue for issue in issues if not issue.get("assignees")]
stale_cutoff = (datetime.now(timezone.utc) - timedelta(days=2)).strftime("%Y-%m-%d")
stale_prs = [pr for pr in pulls if pr.get("updated_at", "")[:10] < stale_cutoff]
overloaded = []
for agent in ("allegro", "codex-agent", "groq", "claude", "ezra", "perplexity", "KimiClaw"):
count = sum(
1
for issue in issues
if agent in [a.get("login", "") for a in (issue.get("assignees") or [])]
)
if count > 3:
overloaded.append((agent, count))
print(" \033[1m\033[4mWARNINGS\033[0m\n")
warns = []
if len(unassigned) > 10:
warns.append(f"{len(unassigned)} unassigned issues across core repos")
if stale_prs:
warns.append(f"{len(stale_prs)} open PRs look stale and may need a review nudge")
for agent, count in overloaded:
warns.append(f"{agent} has {count} assigned issues; rebalance dispatch")
if warns:
for warn in warns:
print(f" \033[33m⚠ {warn}\033[0m")
else:
print(" \033[2m(no major workflow warnings)\033[0m")
if errors:
print("\n \033[1m\033[4mFETCH ERRORS\033[0m\n")
for err in errors[:4]:
print(f" \033[31m{err}\033[0m")
PY
echo ""
# ── GEMINI QUEUE ─────────────────────────────────────────────────────
echo -e " ${B}${U}GEMINI QUEUE${R}"
echo ""
curl -s --max-time 5 -H "Authorization: token $TOKEN" "$API/issues?state=open&limit=50&type=issues" 2>/dev/null | python3 -c "
import json,sys
try:
all_issues = json.loads(sys.stdin.read())
issues = [i for i in all_issues if 'gemini' in [a.get('login','') for a in (i.get('assignees') or [])]]
if not issues: print(' \033[33m⚠ Queue empty — assign issues to gemini\033[0m')
for i in issues[:6]:
n = i['number']
t = i['title'][:55]
print(f' #{n:<4d} {t}')
if len(issues) > 6: print(f' \033[2m... +{len(issues)-6} more\033[0m')
except: print(' \033[31m(error)\033[0m')
" 2>/dev/null
echo ""
# ── WARNINGS ───────────────────────────────────────────────────────────
HERMES_PROCS=$(ps aux | grep -E "hermes.*python" | grep -v grep | wc -l | tr -d ' ')
STUCK_GIT=$(ps aux | grep "git.*push\|git-remote-http" | grep -v grep | wc -l | tr -d ' ')
ORPHAN_PY=$(ps aux | grep "pytest tests/" | grep -v grep | wc -l | tr -d ' ')
UNASSIGNED=$(curl -s --max-time 3 -H "Authorization: token $TOKEN" "$API/issues?state=open&limit=50&type=issues" 2>/dev/null | python3 -c "import json,sys; issues=json.loads(sys.stdin.read()); print(len([i for i in issues if not i.get('assignees')]))" 2>/dev/null)
WARNS=""
[ "$STUCK_GIT" -gt 0 ] && WARNS+=" ${RD}$STUCK_GIT stuck git processes${R}\n"
[ "$ORPHAN_PY" -gt 0 ] && WARNS+=" ${Y}$ORPHAN_PY orphaned pytest runs${R}\n"
[ "${UNASSIGNED:-0}" -gt 10 ] && WARNS+=" ${Y}$UNASSIGNED unassigned issues — feed the queue${R}\n"
if [ -n "$WARNS" ]; then
echo -e " ${B}${U}WARNINGS${R}"
echo ""
echo -e "$WARNS"
fi
echo -e " ${D}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${R}"
echo -e " ${D}hermes sessions: $HERMES_PROCS unassigned: ${UNASSIGNED:-?} ↻ 20s${R}"
echo -e " ${D}repos: $(printf '%s' "$CORE_REPOS" | wc -w | tr -d ' ') refresh via watch or rerun script${R}"