diff --git a/bin/timmy-loopstat.sh b/bin/timmy-loopstat.sh deleted file mode 100755 index 275f81e7..00000000 --- a/bin/timmy-loopstat.sh +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env bash -# ── LOOPSTAT Panel ────────────────────── -# Strategic view: queue, perf, triage, -# recent cycles. 40-col × 50-row pane. -# ──────────────────────────────────────── - -REPO="$HOME/Timmy-Time-dashboard" -QUEUE="$REPO/.loop/queue.json" -RETRO="$REPO/.loop/retro/cycles.jsonl" -TRIAGE_R="$REPO/.loop/retro/triage.jsonl" -DEEP_R="$REPO/.loop/retro/deep-triage.jsonl" -SUMMARY="$REPO/.loop/retro/summary.json" -QUARANTINE="$REPO/.loop/quarantine.json" -STATE="$REPO/.loop/state.json" - -B='\033[1m' ; D='\033[2m' ; R='\033[0m' -G='\033[32m' ; Y='\033[33m' ; RD='\033[31m' -C='\033[36m' ; M='\033[35m' - -W=$(tput cols 2>/dev/null || echo 40) -hr() { printf "${D}"; printf '─%.0s' $(seq 1 "$W"); printf "${R}\n"; } - -while true; do - clear - echo -e "${B}${M} ◈ LOOPSTAT${R} ${D}$(date '+%H:%M')${R}" - hr - - # ── PERFORMANCE ────────────────────── - python3 -c " -import json, os -f = '$SUMMARY' -if not os.path.exists(f): - print(' \033[2m(no perf data yet)\033[0m') - raise SystemExit -s = json.load(open(f)) -rate = s.get('success_rate', 0) -avg = s.get('avg_duration_seconds', 0) -total = s.get('total_cycles', 0) -merged = s.get('total_prs_merged', 0) -added = s.get('total_lines_added', 0) -removed = s.get('total_lines_removed', 0) - -rc = '\033[32m' if rate >= .8 else '\033[33m' if rate >= .5 else '\033[31m' -am, asec = divmod(avg, 60) -print(f' {rc}{rate*100:.0f}%\033[0m ok \033[1m{am:.0f}m{asec:02.0f}s\033[0m avg {total} cyc') -print(f' \033[32m{merged}\033[0m PRs \033[32m+{added}\033[0m/\033[31m-{removed}\033[0m lines') - -bt = s.get('by_type', {}) -parts = [] -for t in ['bug','feature','refactor']: - i = bt.get(t, {}) - if i.get('count', 0): - sr = i.get('success_rate', 0) - parts.append(f'{t[:3]}:{sr*100:.0f}%') -if parts: - print(f' \033[2m{\" \".join(parts)}\033[0m') -" 2>/dev/null - - hr - - # ── QUEUE ──────────────────────────── - echo -e "${B}${Y} QUEUE${R}" - python3 -c " -import json, os -f = '$QUEUE' -if not os.path.exists(f): - print(' \033[2m(no queue yet)\033[0m') - raise SystemExit -q = json.load(open(f)) -if not q: - print(' \033[2m(empty — needs triage)\033[0m') - raise SystemExit - -types = {} -for item in q: - t = item.get('type','?') - types[t] = types.get(t, 0) + 1 -ts = ' '.join(f'{t[0].upper()}:{n}' for t,n in sorted(types.items()) if t != 'philosophy') -print(f' \033[1m{len(q)}\033[0m ready \033[2m{ts}\033[0m') -print() -for i, item in enumerate(q[:8]): - n = item['issue'] - s = item.get('score', 0) - title = item.get('title', '?') - t = item.get('type', '?') - ic = {'bug':'\033[31m●','feature':'\033[32m◆','refactor':'\033[36m○'}.get(t, '\033[2m·') - bar = '█' * s + '░' * (9 - s) - ptr = '\033[1m→' if i == 0 else f'\033[2m{i+1}' - # Truncate title to fit: 40 - 2(pad) - 2(ptr) - 2(ic) - 5(#num) - 1 = 28 - tit = title[:24] - print(f' {ptr}\033[0m {ic}\033[0m \033[33m#{n}\033[0m {tit}') -if len(q) > 8: - print(f' \033[2m +{len(q)-8} more\033[0m') -" 2>/dev/null - - hr - - # ── TRIAGE ─────────────────────────── - echo -e "${B}${G} TRIAGE${R}" - python3 -c " -import json, os -from datetime import datetime, timezone - -cycle = '?' -if os.path.exists('$STATE'): - try: cycle = json.load(open('$STATE')).get('cycle','?') - except: pass - -def ago(ts): - if not ts: return 'never' - try: - dt = datetime.fromisoformat(ts) - if dt.tzinfo is None: - dt = dt.replace(tzinfo=timezone.utc) - m = int((datetime.now(timezone.utc) - dt).total_seconds() / 60) - if m < 60: return f'{m}m ago' - if m < 1440: return f'{m//60}h{m%60}m ago' - return f'{m//1440}d ago' - except: return '?' - -# Fast -fast_ago = 'never' -if os.path.exists('$TRIAGE_R'): - lines = open('$TRIAGE_R').read().strip().splitlines() - if lines: - try: - last = json.loads(lines[-1]) - fast_ago = ago(last.get('timestamp','')) - except: pass - -# Deep -deep_ago = 'never' -timmy = '' -if os.path.exists('$DEEP_R'): - lines = open('$DEEP_R').read().strip().splitlines() - if lines: - try: - last = json.loads(lines[-1]) - deep_ago = ago(last.get('timestamp','')) - timmy = last.get('timmy_feedback','')[:60] - except: pass - -# Next -try: - c = int(cycle) - nf = 5 - (c % 5) - nd = 20 - (c % 20) -except: - nf = nd = '?' - -print(f' Fast {fast_ago:<12s} \033[2mnext:{nf}c\033[0m') -print(f' Deep {deep_ago:<12s} \033[2mnext:{nd}c\033[0m') -if timmy: - # wrap at ~36 chars - print(f' \033[35mTimmy:\033[0m') - t = timmy - while t: - print(f' \033[2m{t[:36]}\033[0m') - t = t[36:] - -# Quarantine -if os.path.exists('$QUARANTINE'): - try: - qd = json.load(open('$QUARANTINE')) - if qd: - qs = ','.join(f'#{k}' for k in list(qd.keys())[:4]) - print(f' \033[31mQuarantined:{len(qd)}\033[0m {qs}') - except: pass -" 2>/dev/null - - hr - - # ── RECENT CYCLES ──────────────────── - echo -e "${B}${D} CYCLES${R}" - python3 -c " -import json, os -f = '$RETRO' -if not os.path.exists(f): - print(' \033[2m(none yet)\033[0m') - raise SystemExit -lines = open(f).read().strip().splitlines() -recent = [] -for l in lines[-12:]: - try: recent.append(json.loads(l)) - except: continue -if not recent: - print(' \033[2m(none yet)\033[0m') - raise SystemExit -for e in reversed(recent): - cy = e.get('cycle','?') - ok = e.get('success', False) - iss = e.get('issue','') - dur = e.get('duration', 0) - pr = e.get('pr','') - reason = e.get('reason','')[:18] - - ic = '\033[32m✓\033[0m' if ok else '\033[31m✗\033[0m' - ds = f'{dur//60}m' if dur else '-' - ix = f'#{iss}' if iss else ' — ' - if ok: - det = f'PR#{pr}' if pr else '' - else: - det = reason - print(f' {ic} {cy:<3} {ix:<5s} {ds:>4s} \033[2m{det}\033[0m') -" 2>/dev/null - - hr - echo -e "${D} ↻ 10s${R}" - sleep 10 -done