- claude-loop: 7 workers default, scales up to 21, 5s cooldown - gemini-loop: rewritten as parallel worker system (3→12), multi-repo, auto-clone, correct CLI flags (-p/--yolo), bash 3.2 compatible - loop-watchdog: monitors all loops every 2min, auto-restarts dead loops, kills zombies, files Gitea issues for unfixable problems - ops-helpers: added ops-wake-watchdog, ops-kill-watchdog - All scripts use file-based PID tracking (bash 3.2 safe) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
215 lines
7.7 KiB
Bash
Executable File
215 lines
7.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ── Dashboard Control Helpers ──────────────────────────────────────────
|
|
# Source this in the controls pane: source ~/.hermes/bin/ops-helpers.sh
|
|
# ───────────────────────────────────────────────────────────────────────
|
|
|
|
export TOKEN=$(cat ~/.hermes/gitea_token_vps 2>/dev/null)
|
|
export GITEA="http://143.198.27.163:3000"
|
|
export REPO_API="$GITEA/api/v1/repos/rockachopa/Timmy-time-dashboard"
|
|
|
|
ops-help() {
|
|
echo ""
|
|
echo -e "\033[1m\033[35m ◈ CONTROLS\033[0m"
|
|
echo -e "\033[2m ──────────────────────────────────────\033[0m"
|
|
echo ""
|
|
echo -e " \033[1mWake Up\033[0m"
|
|
echo " ops-wake-kimi Restart Kimi loop"
|
|
echo " ops-wake-claude Restart Claude loop"
|
|
echo " ops-wake-gemini Restart Gemini loop"
|
|
echo " ops-wake-gateway Restart gateway"
|
|
echo " ops-wake-all Restart everything"
|
|
echo ""
|
|
echo -e " \033[1mManage\033[0m"
|
|
echo " ops-merge PR_NUM Squash-merge a PR"
|
|
echo " ops-assign ISSUE Assign issue to Kimi"
|
|
echo " ops-assign-claude ISSUE [REPO] Assign to Claude"
|
|
echo " ops-audit Run efficiency audit now"
|
|
echo " ops-prs List open PRs"
|
|
echo " ops-queue Show Kimi's queue"
|
|
echo " ops-claude-queue Show Claude's queue"
|
|
echo " ops-gemini-queue Show Gemini's queue"
|
|
echo ""
|
|
echo -e " \033[1mEmergency\033[0m"
|
|
echo " ops-kill-kimi Stop Kimi loop"
|
|
echo " ops-kill-claude Stop Claude loop"
|
|
echo " ops-kill-gemini Stop Gemini loop"
|
|
echo " ops-kill-zombies Kill stuck git/pytest"
|
|
echo ""
|
|
echo -e " \033[1mWatchdog\033[0m"
|
|
echo " ops-wake-watchdog Start loop watchdog"
|
|
echo " ops-kill-watchdog Stop loop watchdog"
|
|
echo ""
|
|
echo -e " \033[2m Type ops-help to see this again\033[0m"
|
|
echo ""
|
|
}
|
|
|
|
ops-wake-kimi() {
|
|
pkill -f "kimi-loop.sh" 2>/dev/null
|
|
sleep 1
|
|
nohup bash ~/.hermes/bin/kimi-loop.sh >> ~/.hermes/logs/kimi-loop.log 2>&1 &
|
|
echo " Kimi loop started (PID $!)"
|
|
}
|
|
|
|
ops-wake-gateway() {
|
|
hermes gateway start 2>&1
|
|
}
|
|
|
|
ops-wake-claude() {
|
|
local workers="${1:-3}"
|
|
pkill -f "claude-loop.sh" 2>/dev/null
|
|
sleep 1
|
|
nohup bash ~/.hermes/bin/claude-loop.sh "$workers" >> ~/.hermes/logs/claude-loop.log 2>&1 &
|
|
echo " Claude loop started — $workers workers (PID $!)"
|
|
}
|
|
|
|
ops-wake-gemini() {
|
|
pkill -f "gemini-loop.sh" 2>/dev/null
|
|
sleep 1
|
|
nohup bash ~/.hermes/bin/gemini-loop.sh >> ~/.hermes/logs/gemini-loop.log 2>&1 &
|
|
echo " Gemini loop started (PID $!)"
|
|
}
|
|
|
|
ops-wake-all() {
|
|
ops-wake-gateway
|
|
sleep 1
|
|
ops-wake-kimi
|
|
sleep 1
|
|
ops-wake-claude
|
|
sleep 1
|
|
ops-wake-gemini
|
|
echo " All services started"
|
|
}
|
|
|
|
ops-merge() {
|
|
local pr=$1
|
|
[ -z "$pr" ] && { echo "Usage: ops-merge PR_NUMBER"; return 1; }
|
|
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
"$REPO_API/pulls/$pr/merge" -d '{"Do":"squash"}' | python3 -c "
|
|
import json,sys
|
|
d=json.loads(sys.stdin.read())
|
|
if 'sha' in d: print(f' ✓ PR #{$pr} merged ({d[\"sha\"][:8]})')
|
|
else: print(f' ✗ {d.get(\"message\",\"unknown error\")}')
|
|
" 2>/dev/null
|
|
}
|
|
|
|
ops-assign() {
|
|
local issue=$1
|
|
[ -z "$issue" ] && { echo "Usage: ops-assign ISSUE_NUMBER"; return 1; }
|
|
curl -s -X PATCH -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
"$REPO_API/issues/$issue" -d '{"assignees":["kimi"]}' | python3 -c "
|
|
import json,sys; d=json.loads(sys.stdin.read()); print(f' ✓ #{$issue} assigned to kimi')
|
|
" 2>/dev/null
|
|
}
|
|
|
|
ops-audit() {
|
|
bash ~/.hermes/bin/efficiency-audit.sh
|
|
}
|
|
|
|
ops-prs() {
|
|
curl -s -H "Authorization: token $TOKEN" "$REPO_API/pulls?state=open&limit=20" | python3 -c "
|
|
import json,sys
|
|
prs=json.loads(sys.stdin.read())
|
|
for p in prs: print(f' #{p[\"number\"]:4d} {p[\"user\"][\"login\"]:8s} {p[\"title\"][:60]}')
|
|
if not prs: print(' (none)')
|
|
" 2>/dev/null
|
|
}
|
|
|
|
ops-queue() {
|
|
curl -s -H "Authorization: token $TOKEN" "$REPO_API/issues?state=open&assignee=kimi&limit=20&type=issues" | python3 -c "
|
|
import json,sys
|
|
issues=json.loads(sys.stdin.read())
|
|
for i in issues: print(f' #{i[\"number\"]:4d} {i[\"title\"][:60]}')
|
|
if not issues: print(' (empty)')
|
|
" 2>/dev/null
|
|
}
|
|
|
|
ops-kill-kimi() {
|
|
pkill -f "kimi-loop.sh" 2>/dev/null
|
|
pkill -f "kimi.*--print" 2>/dev/null
|
|
echo " Kimi stopped"
|
|
}
|
|
|
|
ops-kill-claude() {
|
|
pkill -f "claude-loop.sh" 2>/dev/null
|
|
pkill -f "claude.*--print.*--dangerously" 2>/dev/null
|
|
rm -rf ~/.hermes/logs/claude-locks/*.lock 2>/dev/null
|
|
echo '{}' > ~/.hermes/logs/claude-active.json 2>/dev/null
|
|
echo " Claude stopped (all workers)"
|
|
}
|
|
|
|
ops-kill-gemini() {
|
|
pkill -f "gemini-loop.sh" 2>/dev/null
|
|
pkill -f "gemini.*--print" 2>/dev/null
|
|
echo " Gemini stopped"
|
|
}
|
|
|
|
ops-assign-claude() {
|
|
local issue=$1
|
|
local repo="${2:-rockachopa/Timmy-time-dashboard}"
|
|
[ -z "$issue" ] && { echo "Usage: ops-assign-claude ISSUE_NUMBER [owner/repo]"; return 1; }
|
|
curl -s -X PATCH -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
"$GITEA/api/v1/repos/$repo/issues/$issue" -d '{"assignees":["claude"]}' | python3 -c "
|
|
import json,sys; d=json.loads(sys.stdin.read()); print(f' ✓ #{$issue} assigned to claude')
|
|
" 2>/dev/null
|
|
}
|
|
|
|
ops-claude-queue() {
|
|
python3 -c "
|
|
import json, 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']
|
|
for repo in repos:
|
|
url = f'{base}/api/v1/repos/{repo}/issues?state=open&assignee=claude&limit=20&type=issues'
|
|
try:
|
|
req = urllib.request.Request(url, headers={'Authorization': f'token {token}'})
|
|
resp = urllib.request.urlopen(req, timeout=5)
|
|
issues = json.loads(resp.read())
|
|
for i in issues:
|
|
print(f' #{i[\"number\"]:4d} {repo.split(\"/\")[1]:20s} {i[\"title\"][:50]}')
|
|
except: continue
|
|
" 2>/dev/null || echo " (error)"
|
|
}
|
|
|
|
ops-assign-gemini() {
|
|
local issue=$1
|
|
local repo="${2:-rockachopa/Timmy-time-dashboard}"
|
|
[ -z "$issue" ] && { echo "Usage: ops-assign-gemini ISSUE_NUMBER [owner/repo]"; return 1; }
|
|
curl -s -X PATCH -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
"$GITEA/api/v1/repos/$repo/issues/$issue" -d '{"assignees":["gemini"]}' | python3 -c "
|
|
import json,sys; d=json.loads(sys.stdin.read()); print(f' ✓ #{$issue} assigned to gemini')
|
|
" 2>/dev/null
|
|
}
|
|
|
|
ops-gemini-queue() {
|
|
curl -s -H "Authorization: token $TOKEN" "$REPO_API/issues?state=open&assignee=gemini&limit=20&type=issues" | python3 -c "
|
|
import json,sys
|
|
issues=json.loads(sys.stdin.read())
|
|
for i in issues: print(f' #{i[\"number\"]:4d} {i[\"title\"][:60]}')
|
|
if not issues: print(' (empty)')
|
|
" 2>/dev/null
|
|
}
|
|
|
|
ops-kill-zombies() {
|
|
local killed=0
|
|
for pid in $(ps aux | grep "pytest tests/" | grep -v grep | awk '{print $2}'); do
|
|
kill "$pid" 2>/dev/null && killed=$((killed+1))
|
|
done
|
|
for pid in $(ps aux | grep "git.*push\|git-remote-http" | grep -v grep | awk '{print $2}'); do
|
|
kill "$pid" 2>/dev/null && killed=$((killed+1))
|
|
done
|
|
echo " Killed $killed zombie processes"
|
|
}
|
|
|
|
ops-wake-watchdog() {
|
|
pkill -f "loop-watchdog.sh" 2>/dev/null
|
|
sleep 1
|
|
nohup bash ~/.hermes/bin/loop-watchdog.sh >> ~/.hermes/logs/watchdog.log 2>&1 &
|
|
echo " Watchdog started (PID $!)"
|
|
}
|
|
|
|
ops-kill-watchdog() {
|
|
pkill -f "loop-watchdog.sh" 2>/dev/null
|
|
echo " Watchdog stopped"
|
|
}
|