#!/usr/bin/env bash # ── Timmy Loop Status Panel ──────────────────────────────────────────── # Compact, info-dense sidebar for the tmux development loop. # Refreshes every 10s. Designed for ~40-col wide pane. # ─────────────────────────────────────────────────────────────────────── STATE="$HOME/Timmy-Time-dashboard/.loop/state.json" REPO="$HOME/Timmy-Time-dashboard" TOKEN=$(cat ~/.hermes/gitea_token 2>/dev/null) API="http://143.198.27.163:3000/api/v1/repos/rockachopa/Timmy-time-dashboard" # ── Colors ── B='\033[1m' # bold D='\033[2m' # dim R='\033[0m' # reset G='\033[32m' # green Y='\033[33m' # yellow RD='\033[31m' # red C='\033[36m' # cyan M='\033[35m' # magenta W='\033[37m' # white BG='\033[42;30m' # green bg BY='\033[43;30m' # yellow bg BR='\033[41;37m' # red bg # How wide is our pane? COLS=$(tput cols 2>/dev/null || echo 40) hr() { printf "${D}"; printf '─%.0s' $(seq 1 "$COLS"); printf "${R}\n"; } while true; do clear # ── Header ── echo -e "${B}${C} ⚙ TIMMY DEV LOOP${R} ${D}$(date '+%H:%M:%S')${R}" hr # ── Loop State ── if [ -f "$STATE" ]; then eval "$(python3 -c " import json, sys with open('$STATE') as f: s = json.load(f) print(f'CYCLE={s.get(\"cycle\",\"?\")}')" 2>/dev/null)" STATUS=$(python3 -c "import json; print(json.load(open('$STATE'))['status'])" 2>/dev/null || echo "?") LAST_OK=$(python3 -c " import json from datetime import datetime, timezone s = json.load(open('$STATE')) t = s.get('last_completed','') if t: dt = datetime.fromisoformat(t.replace('Z','+00:00')) delta = datetime.now(timezone.utc) - dt mins = int(delta.total_seconds() / 60) if mins < 60: print(f'{mins}m ago') else: print(f'{mins//60}h {mins%60}m ago') else: print('never') " 2>/dev/null || echo "?") CLOSED=$(python3 -c "import json; print(len(json.load(open('$STATE')).get('issues_closed',[])))" 2>/dev/null || echo 0) CREATED=$(python3 -c "import json; print(len(json.load(open('$STATE')).get('issues_created',[])))" 2>/dev/null || echo 0) ERRS=$(python3 -c "import json; print(len(json.load(open('$STATE')).get('errors',[])))" 2>/dev/null || echo 0) LAST_ISSUE=$(python3 -c "import json; print(json.load(open('$STATE')).get('last_issue','—'))" 2>/dev/null || echo "—") LAST_PR=$(python3 -c "import json; print(json.load(open('$STATE')).get('last_pr','—'))" 2>/dev/null || echo "—") TESTS=$(python3 -c " import json s = json.load(open('$STATE')) t = s.get('test_results',{}) if t: print(f\"{t.get('passed',0)} pass, {t.get('failed',0)} fail, {t.get('coverage','?')} cov\") else: print('no data') " 2>/dev/null || echo "no data") # Status badge case "$STATUS" in working) BADGE="${BY} WORKING ${R}" ;; idle) BADGE="${BG} IDLE ${R}" ;; error) BADGE="${BR} ERROR ${R}" ;; *) BADGE="${D} $STATUS ${R}" ;; esac echo -e " ${B}Status${R} $BADGE ${D}cycle${R} ${B}$CYCLE${R}" echo -e " ${B}Last OK${R} ${G}$LAST_OK${R} ${D}issue${R} #$LAST_ISSUE ${D}PR${R} #$LAST_PR" echo -e " ${G}✓${R} $CLOSED closed ${C}+${R} $CREATED created ${RD}✗${R} $ERRS errs" echo -e " ${D}Tests:${R} $TESTS" else echo -e " ${RD}No state file${R}" fi hr # ── Ollama Status ── echo -e " ${B}${M}◆ OLLAMA${R}" OLLAMA_PS=$(curl -s http://localhost:11434/api/ps 2>/dev/null) if [ -n "$OLLAMA_PS" ] && echo "$OLLAMA_PS" | python3 -c "import sys,json; json.load(sys.stdin)" &>/dev/null; then python3 -c " import json, sys data = json.loads('''$OLLAMA_PS''') models = data.get('models', []) if not models: print(' \033[2m(no models loaded)\033[0m') for m in models: name = m.get('name','?') vram = m.get('size_vram', 0) / 1e9 exp = m.get('expires_at','') print(f' \033[32m●\033[0m {name} \033[2m{vram:.1f}GB VRAM\033[0m') " 2>/dev/null else echo -e " ${RD}● offline${R}" fi # ── Timmy Health ── TIMMY_HEALTH=$(curl -s --max-time 2 http://localhost:8000/health 2>/dev/null) if [ -n "$TIMMY_HEALTH" ]; then python3 -c " import json h = json.loads('''$TIMMY_HEALTH''') status = h.get('status','?') ollama = h.get('services',{}).get('ollama','?') model = h.get('llm_model','?') agent_st = list(h.get('agents',{}).values())[0].get('status','?') if h.get('agents') else '?' up = int(h.get('uptime_seconds',0)) hrs, rem = divmod(up, 3600) mins = rem // 60 print(f' \033[1m\033[35m◆ TIMMY DASHBOARD\033[0m') print(f' \033[32m●\033[0m {status} model={model}') print(f' \033[2magent={agent_st} ollama={ollama} up={hrs}h{mins}m\033[0m') " 2>/dev/null else echo -e " ${B}${M}◆ TIMMY DASHBOARD${R}" echo -e " ${RD}● unreachable${R}" fi hr # ── Open Issues ── echo -e " ${B}${Y}▶ OPEN ISSUES${R}" if [ -n "$TOKEN" ]; then curl -s "${API}/issues?state=open&limit=10&sort=created&direction=desc" \ -H "Authorization: token $TOKEN" 2>/dev/null | \ python3 -c " import json, sys try: issues = json.load(sys.stdin) if not issues: print(' \033[2m(none)\033[0m') for i in issues[:10]: num = i['number'] title = i['title'][:36] labels = ','.join(l['name'][:8] for l in i.get('labels',[])) lbl = f' \033[2m[{labels}]\033[0m' if labels else '' print(f' \033[33m#{num:<4d}\033[0m {title}{lbl}') if len(issues) > 10: print(f' \033[2m... +{len(issues)-10} more\033[0m') except: print(' \033[2m(fetch failed)\033[0m') " 2>/dev/null else echo -e " ${RD}(no token)${R}" fi # ── Open PRs ── echo -e " ${B}${G}▶ OPEN PRs${R}" if [ -n "$TOKEN" ]; then curl -s "${API}/pulls?state=open&limit=5" \ -H "Authorization: token $TOKEN" 2>/dev/null | \ python3 -c " import json, sys try: prs = json.load(sys.stdin) if not prs: print(' \033[2m(none)\033[0m') for p in prs[:5]: num = p['number'] title = p['title'][:36] print(f' \033[32mPR #{num:<4d}\033[0m {title}') except: print(' \033[2m(fetch failed)\033[0m') " 2>/dev/null else echo -e " ${RD}(no token)${R}" fi hr # ── Git Log ── echo -e " ${B}${D}▶ RECENT COMMITS${R}" cd "$REPO" 2>/dev/null && git log --oneline --no-decorate -6 2>/dev/null | while read line; do HASH=$(echo "$line" | cut -c1-7) MSG=$(echo "$line" | cut -c9- | cut -c1-32) echo -e " ${C}${HASH}${R} ${D}${MSG}${R}" done hr # ── Claims ── CLAIMS_FILE="$REPO/.loop/claims.json" if [ -f "$CLAIMS_FILE" ]; then CLAIMS=$(python3 -c " import json with open('$CLAIMS_FILE') as f: c = json.load(f) active = [(k,v) for k,v in c.items() if v.get('status') == 'active'] if active: for k,v in active: print(f' \033[33m⚡\033[0m #{k} claimed by {v.get(\"agent\",\"?\")[:12]}') else: print(' \033[2m(none active)\033[0m') " 2>/dev/null) if [ -n "$CLAIMS" ]; then echo -e " ${B}${Y}▶ CLAIMED${R}" echo "$CLAIMS" fi fi # ── System ── echo -e " ${B}${D}▶ SYSTEM${R}" # Disk DISK=$(df -h / 2>/dev/null | tail -1 | awk '{print $4 " free / " $2}') echo -e " ${D}Disk:${R} $DISK" # Memory (macOS) if command -v memory_pressure &>/dev/null; then MEM_PRESS=$(memory_pressure 2>/dev/null | grep "System-wide" | head -1 | sed 's/.*: //') echo -e " ${D}Mem:${R} $MEM_PRESS" elif [ -f /proc/meminfo ]; then MEM=$(awk '/MemAvailable/{printf "%.1fGB free", $2/1048576}' /proc/meminfo 2>/dev/null) echo -e " ${D}Mem:${R} $MEM" fi # CPU load LOAD=$(uptime | sed 's/.*averages: //' | cut -d',' -f1 | xargs) echo -e " ${D}Load:${R} $LOAD" hr # ── Notes from last cycle ── if [ -f "$STATE" ]; then NOTES=$(python3 -c " import json s = json.load(open('$STATE')) n = s.get('notes','') if n: lines = n[:150] if len(n) > 150: lines += '...' print(lines) " 2>/dev/null) if [ -n "$NOTES" ]; then echo -e " ${B}${D}▶ LAST CYCLE NOTE${R}" echo -e " ${D}${NOTES}${R}" hr fi # Timmy observations TIMMY_OBS=$(python3 -c " import json s = json.load(open('$STATE')) obs = s.get('timmy_observations','') if obs: lines = obs[:120] if len(obs) > 120: lines += '...' print(lines) " 2>/dev/null) if [ -n "$TIMMY_OBS" ]; then echo -e " ${B}${M}▶ TIMMY SAYS${R}" echo -e " ${D}${TIMMY_OBS}${R}" hr fi fi # ── Watchdog: restart loop if it died ────────────────────────────── LOOP_LOCK="/tmp/timmy-loop.lock" if [ -f "$LOOP_LOCK" ]; then LOOP_PID=$(cat "$LOOP_LOCK" 2>/dev/null) if ! kill -0 "$LOOP_PID" 2>/dev/null; then echo -e " ${BR} ⚠ LOOP DIED — RESTARTING ${R}" rm -f "$LOOP_LOCK" tmux send-keys -t "dev:2.1" "bash ~/.hermes/bin/timmy-loop.sh" Enter 2>/dev/null fi else # No lock file at all — loop never started or was killed if ! pgrep -f "timmy-loop.sh" >/dev/null 2>&1; then echo -e " ${BR} ⚠ LOOP NOT RUNNING — STARTING ${R}" tmux send-keys -t "dev:2.1" "bash ~/.hermes/bin/timmy-loop.sh" Enter 2>/dev/null fi fi echo -e " ${D}↻ 8s${R}" sleep 8 done