feat: add Gemini feed pane and stats to ops dashboard
- 3 equal stacked feed panes on right (Kimi/Claude/Gemini) - Gemini service status, stats, and queue in ops-panel - Gemini live log feed with color highlighting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# ── Hermes Ops Dashboard v2 ────────────────────────────────────────────
|
# ── Hermes Ops Dashboard v2 ────────────────────────────────────────────
|
||||||
# 4-pane layout with dual agent feeds:
|
# 5-pane layout with triple agent feeds:
|
||||||
# ┌───────────────────────────────────┬────────────────────────────────┐
|
# ┌───────────────────────────────────┬────────────────────────────────┐
|
||||||
# │ │ KIMI LIVE FEED │
|
# │ │ KIMI LIVE FEED │
|
||||||
# │ STATUS + GITEA + QUEUE │ (tail log, colored) │
|
# │ STATUS + GITEA + QUEUE ├────────────────────────────────┤
|
||||||
# │ (auto-refresh 20s) ├────────────────────────────────┤
|
# │ (auto-refresh 20s) │ CLAUDE LIVE FEED │
|
||||||
# │ │ CLAUDE LIVE FEED │
|
# │ ├────────────────────────────────┤
|
||||||
# │ │ (tail log, colored) │
|
# │ │ GEMINI LIVE FEED │
|
||||||
# ├───────────────────────────────────┴────────────────────────────────┤
|
# ├───────────────────────────────────┴────────────────────────────────┤
|
||||||
# │ CONTROLS (bash prompt with helpers loaded) │
|
# │ CONTROLS (bash prompt with helpers loaded) │
|
||||||
# └────────────────────────────────────────────────────────────────────┘
|
# └────────────────────────────────────────────────────────────────────┘
|
||||||
@@ -23,12 +23,14 @@ tmux split-window -h -p 45 -t "$SESSION"
|
|||||||
# Split left pane: top status (80%) | bottom controls (20%)
|
# Split left pane: top status (80%) | bottom controls (20%)
|
||||||
tmux split-window -v -p 20 -t "$SESSION:1.1"
|
tmux split-window -v -p 20 -t "$SESSION:1.1"
|
||||||
|
|
||||||
# Split right pane: top kimi (50%) | bottom claude (50%)
|
# Split right pane into 3 equal feeds: kimi (top 33%) | claude (mid 33%) | gemini (bot 33%)
|
||||||
tmux split-window -v -p 50 -t "$SESSION:1.2"
|
tmux split-window -v -p 67 -t "$SESSION:1.2"
|
||||||
|
tmux split-window -v -p 50 -t "$SESSION:1.3"
|
||||||
|
|
||||||
# Initialize log files if they don't exist
|
# Initialize log files if they don't exist
|
||||||
touch ~/.hermes/logs/kimi-loop.log 2>/dev/null
|
touch ~/.hermes/logs/kimi-loop.log 2>/dev/null
|
||||||
touch ~/.hermes/logs/claude-loop.log 2>/dev/null
|
touch ~/.hermes/logs/claude-loop.log 2>/dev/null
|
||||||
|
touch ~/.hermes/logs/gemini-loop.log 2>/dev/null
|
||||||
|
|
||||||
# Pane 1 (top-left): consolidated status, auto-refresh
|
# Pane 1 (top-left): consolidated status, auto-refresh
|
||||||
tmux send-keys -t "$SESSION:1.1" "watch -n 20 -t -c 'bash ~/.hermes/bin/ops-panel.sh'" Enter
|
tmux send-keys -t "$SESSION:1.1" "watch -n 20 -t -c 'bash ~/.hermes/bin/ops-panel.sh'" Enter
|
||||||
@@ -36,17 +38,21 @@ tmux send-keys -t "$SESSION:1.1" "watch -n 20 -t -c 'bash ~/.hermes/bin/ops-pane
|
|||||||
# Pane 2 (top-right): kimi live feed with color
|
# Pane 2 (top-right): kimi live feed with color
|
||||||
tmux send-keys -t "$SESSION:1.2" "echo -e '\\033[1m\\033[33m KIMI FEED\\033[0m' && tail -f ~/.hermes/logs/kimi-loop.log | GREP_COLOR='1;32' grep --color=always -E 'SUCCESS|$' | GREP_COLOR='1;31' grep --color=always -E 'FAILED|BACKOFF|$' | GREP_COLOR='1;36' grep --color=always -E 'ISSUE #[0-9]+|$'" Enter
|
tmux send-keys -t "$SESSION:1.2" "echo -e '\\033[1m\\033[33m KIMI FEED\\033[0m' && tail -f ~/.hermes/logs/kimi-loop.log | GREP_COLOR='1;32' grep --color=always -E 'SUCCESS|$' | GREP_COLOR='1;31' grep --color=always -E 'FAILED|BACKOFF|$' | GREP_COLOR='1;36' grep --color=always -E 'ISSUE #[0-9]+|$'" Enter
|
||||||
|
|
||||||
# Pane 3 (bottom-right): claude live feed with color
|
# Pane 3 (mid-right): claude live feed with color
|
||||||
tmux send-keys -t "$SESSION:1.3" "echo -e '\\033[1m\\033[35m CLAUDE FEED\\033[0m' && tail -f ~/.hermes/logs/claude-loop.log | GREP_COLOR='1;32' grep --color=always -E 'SUCCESS|$' | GREP_COLOR='1;31' grep --color=always -E 'FAILED|BACKOFF|$' | GREP_COLOR='1;36' grep --color=always -E 'ISSUE #[0-9]+|$'" Enter
|
tmux send-keys -t "$SESSION:1.3" "echo -e '\\033[1m\\033[35m CLAUDE FEED\\033[0m' && tail -f ~/.hermes/logs/claude-loop.log | GREP_COLOR='1;32' grep --color=always -E 'SUCCESS|$' | GREP_COLOR='1;31' grep --color=always -E 'FAILED|BACKOFF|$' | GREP_COLOR='1;36' grep --color=always -E 'ISSUE #[0-9]+|$'" Enter
|
||||||
|
|
||||||
# Pane 4 (bottom-left): controls with helpers sourced
|
# Pane 4 (bottom-right): gemini live feed with color
|
||||||
tmux send-keys -t "$SESSION:1.4" "source ~/.hermes/bin/ops-helpers.sh && ops-help" Enter
|
tmux send-keys -t "$SESSION:1.4" "echo -e '\\033[1m\\033[32m GEMINI FEED\\033[0m' && tail -f ~/.hermes/logs/gemini-loop.log | GREP_COLOR='1;32' grep --color=always -E 'SUCCESS|$' | GREP_COLOR='1;31' grep --color=always -E 'FAILED|BACKOFF|$' | GREP_COLOR='1;36' grep --color=always -E 'ISSUE #[0-9]+|$'" Enter
|
||||||
|
|
||||||
|
# Pane 5 (bottom-left): controls with helpers sourced
|
||||||
|
tmux send-keys -t "$SESSION:1.5" "source ~/.hermes/bin/ops-helpers.sh && ops-help" Enter
|
||||||
|
|
||||||
# Set pane titles
|
# Set pane titles
|
||||||
tmux select-pane -t "$SESSION:1.1" -T "Status"
|
tmux select-pane -t "$SESSION:1.1" -T "Status"
|
||||||
tmux select-pane -t "$SESSION:1.2" -T "Kimi Feed"
|
tmux select-pane -t "$SESSION:1.2" -T "Kimi Feed"
|
||||||
tmux select-pane -t "$SESSION:1.3" -T "Claude Feed"
|
tmux select-pane -t "$SESSION:1.3" -T "Claude Feed"
|
||||||
tmux select-pane -t "$SESSION:1.4" -T "Controls"
|
tmux select-pane -t "$SESSION:1.4" -T "Gemini Feed"
|
||||||
|
tmux select-pane -t "$SESSION:1.5" -T "Controls"
|
||||||
|
|
||||||
# Border styling
|
# Border styling
|
||||||
tmux set-option -t "$SESSION" pane-border-status top
|
tmux set-option -t "$SESSION" pane-border-status top
|
||||||
@@ -55,6 +61,6 @@ tmux set-option -t "$SESSION" pane-border-style "fg=colour240"
|
|||||||
tmux set-option -t "$SESSION" pane-active-border-style "fg=cyan"
|
tmux set-option -t "$SESSION" pane-active-border-style "fg=cyan"
|
||||||
|
|
||||||
# Focus on controls pane
|
# Focus on controls pane
|
||||||
tmux select-pane -t "$SESSION:1.4"
|
tmux select-pane -t "$SESSION:1.5"
|
||||||
|
|
||||||
tmux attach -t "$SESSION"
|
tmux attach -t "$SESSION"
|
||||||
|
|||||||
@@ -49,6 +49,19 @@ else
|
|||||||
echo -e " ${FAIL} Claude Loop ${RD}DOWN — run: ops-wake-claude${R}"
|
echo -e " ${FAIL} Claude Loop ${RD}DOWN — run: ops-wake-claude${R}"
|
||||||
fi
|
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
|
||||||
|
else
|
||||||
|
echo -e " ${FAIL} Gemini Loop ${RD}DOWN — run: ops-wake-gemini${R}"
|
||||||
|
fi
|
||||||
|
|
||||||
# Gitea VPS
|
# Gitea VPS
|
||||||
if curl -s --max-time 3 "http://143.198.27.163:3000/api/v1/version" >/dev/null 2>&1; then
|
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}"
|
echo -e " ${OK} Gitea VPS ${D}143.198.27.163:3000${R}"
|
||||||
@@ -131,6 +144,26 @@ else
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# ── 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 || echo 0)
|
||||||
|
GM_FAILED=$(grep -c "FAILED:" "$GEMINI_LOG" 2>/dev/null || 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 ""
|
||||||
|
|
||||||
# ── OPEN PRS ───────────────────────────────────────────────────────────
|
# ── OPEN PRS ───────────────────────────────────────────────────────────
|
||||||
echo -e " ${B}${U}PULL REQUESTS${R}"
|
echo -e " ${B}${U}PULL REQUESTS${R}"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -217,6 +250,23 @@ else:
|
|||||||
" 2>/dev/null
|
" 2>/dev/null
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# ── GEMINI QUEUE ─────────────────────────────────────────────────────
|
||||||
|
echo -e " ${B}${U}GEMINI QUEUE${R}"
|
||||||
|
echo ""
|
||||||
|
curl -s --max-time 5 -H "Authorization: token $TOKEN" "$API/issues?state=open&assignee=gemini&limit=10&type=issues" 2>/dev/null | python3 -c "
|
||||||
|
import json,sys
|
||||||
|
try:
|
||||||
|
issues = json.loads(sys.stdin.read())
|
||||||
|
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 ───────────────────────────────────────────────────────────
|
# ── WARNINGS ───────────────────────────────────────────────────────────
|
||||||
HERMES_PROCS=$(ps aux | grep -E "hermes.*python" | grep -v grep | wc -l | tr -d ' ')
|
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 ' ')
|
STUCK_GIT=$(ps aux | grep "git.*push\|git-remote-http" | grep -v grep | wc -l | tr -d ' ')
|
||||||
|
|||||||
Reference in New Issue
Block a user