Files
timmy-home/uniwizard/kimi-heartbeat.sh

111 lines
4.5 KiB
Bash
Executable File

#!/bin/bash
# kimi-heartbeat.sh — Polls Gitea for assigned-kimi tickets, dispatches to OpenClaw
# Run as: bash ~/.timmy/uniwizard/kimi-heartbeat.sh
# Or as a cron: every 5m
set -euo pipefail
TOKEN=$(cat /Users/apayne/.timmy/kimi_gitea_token | tr -d '[:space:]')
BASE="http://100.126.61.75:3000/api/v1"
LOG="/tmp/kimi-heartbeat.log"
log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG"; }
# Find all issues labeled "assigned-kimi" across repos
REPOS=("Timmy_Foundation/timmy-home" "Timmy_Foundation/timmy-config" "Timmy_Foundation/the-nexus")
for repo in "${REPOS[@]}"; do
# Get issues with assigned-kimi label but NOT kimi-in-progress or kimi-done
issues=$(curl -s -H "Authorization: token $TOKEN" \
"$BASE/repos/$repo/issues?state=open&labels=assigned-kimi&limit=10" | \
python3 -c "
import json, sys
issues = json.load(sys.stdin)
for i in issues:
labels = [l['name'] for l in i.get('labels',[])]
# Skip if already in-progress or done
if 'kimi-in-progress' in labels or 'kimi-done' in labels:
continue
body = (i.get('body','') or '')[:500].replace('\n',' ')
print(f\"{i['number']}|{i['title']}|{body}\")
" 2>/dev/null)
if [ -z "$issues" ]; then
continue
fi
while IFS='|' read -r issue_num title body; do
[ -z "$issue_num" ] && continue
log "DISPATCH: $repo #$issue_num$title"
# Add kimi-in-progress label
# First get the label ID
label_id=$(curl -s -H "Authorization: token $TOKEN" \
"$BASE/repos/$repo/labels" | \
python3 -c "import json,sys; [print(l['id']) for l in json.load(sys.stdin) if l['name']=='kimi-in-progress']" 2>/dev/null)
if [ -n "$label_id" ]; then
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d "{\"labels\":[$label_id]}" \
"$BASE/repos/$repo/issues/$issue_num/labels" > /dev/null 2>&1
fi
# Post "picking up" comment
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d "{\"body\":\"🟠 **Kimi picking up this task** via OpenClaw heartbeat.\\nBackend: kimi/kimi-code\\nTimestamp: $(date -u '+%Y-%m-%dT%H:%M:%SZ')\"}" \
"$BASE/repos/$repo/issues/$issue_num/comments" > /dev/null 2>&1
# Dispatch to OpenClaw
# Build a self-contained prompt from the issue
prompt="You are Timmy, working on $repo issue #$issue_num: $title
ISSUE BODY:
$body
YOUR TASK:
1. Read the issue carefully
2. Do the work described — create files, write code, analyze, review as needed
3. Work in ~/.timmy/uniwizard/ for new files
4. When done, post a summary of what you did as a comment on the Gitea issue
Gitea API: $BASE, token in /Users/apayne/.config/gitea/token
Repo: $repo, Issue: $issue_num
5. Be thorough but practical. Ship working code."
# Fire via openclaw agent (async via background)
(
result=$(openclaw agent --agent main --message "$prompt" --json 2>/dev/null)
status=$(echo "$result" | python3 -c "import json,sys; print(json.load(sys.stdin).get('status','error'))" 2>/dev/null)
if [ "$status" = "ok" ]; then
log "COMPLETED: $repo #$issue_num"
# Swap kimi-in-progress for kimi-done
done_id=$(curl -s -H "Authorization: token $TOKEN" \
"$BASE/repos/$repo/labels" | \
python3 -c "import json,sys; [print(l['id']) for l in json.load(sys.stdin) if l['name']=='kimi-done']" 2>/dev/null)
progress_id=$(curl -s -H "Authorization: token $TOKEN" \
"$BASE/repos/$repo/labels" | \
python3 -c "import json,sys; [print(l['id']) for l in json.load(sys.stdin) if l['name']=='kimi-in-progress']" 2>/dev/null)
[ -n "$progress_id" ] && curl -s -X DELETE -H "Authorization: token $TOKEN" \
"$BASE/repos/$repo/issues/$issue_num/labels/$progress_id" > /dev/null 2>&1
[ -n "$done_id" ] && curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d "{\"labels\":[$done_id]}" \
"$BASE/repos/$repo/issues/$issue_num/labels" > /dev/null 2>&1
else
log "FAILED: $repo #$issue_num$status"
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d "{\"body\":\"🔴 **Kimi failed on this task.**\\nStatus: $status\\nTimestamp: $(date -u '+%Y-%m-%dT%H:%M:%SZ')\"}" \
"$BASE/repos/$repo/issues/$issue_num/comments" > /dev/null 2>&1
fi
) &
log "DISPATCHED: $repo #$issue_num (background PID $!)"
# Don't flood — wait 5s between dispatches
sleep 5
done <<< "$issues"
done
log "Heartbeat complete. $(date)"