#!/bin/bash # Mimo Swarm Worker — One-shot execution # Receives a prompt file, runs mimo-v2-pro via hermes, handles the git workflow. # # Usage: mimo-worker.sh # The prompt file contains all instructions for the worker. set -euo pipefail PROMPT_FILE="${1:?Usage: mimo-worker.sh }" WORKER_ID=$(basename "$PROMPT_FILE" .txt | sed 's/prompt-//') LOG_DIR="$HOME/.hermes/mimo-swarm/logs" LOG_FILE="$LOG_DIR/worker-${WORKER_ID}.log" STATE_DIR="$HOME/.hermes/mimo-swarm/state" GITEA_URL="https://forge.alexanderwhitestone.com" TOKEN=$(cat "$HOME/.config/gitea/token") log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" | tee -a "$LOG_FILE" } # Read the prompt if [ ! -f "$PROMPT_FILE" ]; then log "ERROR: Prompt file not found: $PROMPT_FILE" exit 1 fi PROMPT=$(cat "$PROMPT_FILE") log "WORKER START: $WORKER_ID" # Extract repo and issue from prompt REPO=$(echo "$PROMPT" | grep "^Repository:" | head -1 | awk '{print $2}') ISSUE_NUM=$(echo "$PROMPT" | grep "^Issue:" | head -1 | awk '{print $2}' | tr -d '#') LANE=$(echo "$WORKER_ID" | cut -d- -f2) BRANCH="mimo/${LANE}/issue-${ISSUE_NUM}" WORK_DIR="/tmp/${WORKER_ID}" log " Repo: $REPO | Issue: #$ISSUE_NUM | Branch: $BRANCH" # Clone the repo mkdir -p "$(dirname "$WORK_DIR")" if [ -d "$WORK_DIR" ]; then log " Pulling existing clone..." cd "$WORK_DIR" git fetch origin main 2>/dev/null || true git checkout main 2>/dev/null || git checkout master 2>/dev/null || true git pull 2>/dev/null || true else log " Cloning..." CLONE_URL="${GITEA_URL}/${REPO}.git" git clone "$CLONE_URL" "$WORK_DIR" 2>>"$LOG_FILE" cd "$WORK_DIR" fi # Create branch git checkout -b "$BRANCH" 2>/dev/null || git checkout "$BRANCH" log " On branch: $BRANCH" # Run mimo via hermes log " Dispatching to mimo-v2-pro..." hermes chat -q "$PROMPT" --provider nous -m xiaomi/mimo-v2-pro --yolo -t terminal,code_execution -Q >>"$LOG_FILE" 2>&1 MIMO_EXIT=$? log " Mimo exited with code: $MIMO_EXIT" # Quality gate log " Running quality gate..." # Check if there are changes CHANGES=$(git diff --stat 2>/dev/null || echo "") STAGED=$(git status --porcelain 2>/dev/null || echo "") if [ -z "$CHANGES" ] && [ -z "$STAGED" ]; then log " QUALITY GATE: No changes detected. Worker produced nothing." # Try to salvage - maybe changes were committed already COMMITS=$(git log main..HEAD --oneline 2>/dev/null | wc -l | tr -d ' ') if [ "$COMMITS" -gt 0 ]; then log " SALVAGE: Found $COMMITS commit(s) on branch. Proceeding to push." else log " ABANDON: No commits, no changes. Nothing to salvage." cd /tmp rm -rf "$WORK_DIR" # Write release state echo "{\"status\":\"abandoned\",\"reason\":\"no_changes\",\"worker\":\"$WORKER_ID\",\"issue\":$ISSUE_NUM}" > "$STATE_DIR/result-${WORKER_ID}.json" exit 0 fi else # Syntax check for Python files PY_FILES=$(find . -name "*.py" -newer .git/HEAD 2>/dev/null | head -20) for pyf in $PY_FILES; do if ! python3 -m py_compile "$pyf" 2>>"$LOG_FILE"; then log " SYNTAX ERROR in $pyf — attempting fix or committing anyway" fi done # Syntax check for JS files JS_FILES=$(find . -name "*.js" -newer .git/HEAD 2>/dev/null | head -20) for jsf in $JS_FILES; do if ! node --check "$jsf" 2>>"$LOG_FILE"; then log " SYNTAX ERROR in $jsf — attempting fix or committing anyway" fi done # Diff size check DIFF_LINES=$(git diff --stat | tail -1 | grep -oP '\d+ insertion' | grep -oP '\d+' || echo "0") if [ "$DIFF_LINES" -gt 500 ]; then log " WARNING: Large diff ($DIFF_LINES insertions). Committing but flagging for review." fi # Commit git add -A COMMIT_MSG="fix: $(echo "$PROMPT" | grep '^Title:' | sed 's/^Title: //') (closes #${ISSUE_NUM})" git commit -m "$COMMIT_MSG" 2>>"$LOG_FILE" || log " Nothing to commit (already clean)" fi # Push log " Pushing branch..." PUSH_OUTPUT=$(git push origin "$BRANCH" 2>&1) || { log " Push failed, trying force push..." git push -f origin "$BRANCH" 2>>"$LOG_FILE" || log " Push failed completely" } log " Pushed: $PUSH_OUTPUT" # Create PR log " Creating PR..." PR_TITLE="fix: $(echo "$PROMPT" | grep '^Title:' | sed 's/^Title: //')" PR_BODY="Closes #${ISSUE_NUM} Automated by mimo-v2-pro swarm worker. Worker: ${WORKER_ID}" PR_RESPONSE=$(curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/pulls" \ -H "Authorization: token ${TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"title\":\"${PR_TITLE}\",\"head\":\"${BRANCH}\",\"base\":\"main\",\"body\":\"${PR_BODY}\"}" 2>>"$LOG_FILE") PR_NUM=$(echo "$PR_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('number','?'))" 2>/dev/null || echo "?") log " PR created: #${PR_NUM}" # Clean up cd /tmp # Keep work dir for debugging, clean later # Write result cat > "$STATE_DIR/result-${WORKER_ID}.json" <