158 lines
5.2 KiB
Bash
Executable File
158 lines
5.2 KiB
Bash
Executable File
#!/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 <prompt_file>
|
|
# The prompt file contains all instructions for the worker.
|
|
|
|
set -euo pipefail
|
|
|
|
PROMPT_FILE="${1:?Usage: mimo-worker.sh <prompt_file>}"
|
|
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" <<EOF
|
|
{
|
|
"status": "completed",
|
|
"worker": "$WORKER_ID",
|
|
"repo": "$REPO",
|
|
"issue": $ISSUE_NUM,
|
|
"branch": "$BRANCH",
|
|
"pr": $PR_NUM,
|
|
"mimo_exit": $MIMO_EXIT,
|
|
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
}
|
|
EOF
|
|
|
|
log "WORKER COMPLETE: $WORKER_ID → PR #${PR_NUM}"
|