| name: Sovereign Swarm — Multi-Repo Agent | |
| on: | |
| schedule: | |
| - cron: '0 */6 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| task: | |
| description: 'Task to run across all repos (leave blank for auto-scan)' | |
| required: false | |
| default: '' | |
| target_repo: | |
| description: 'Single repo to target (leave blank for all)' | |
| required: false | |
| default: '' | |
| permissions: | |
| contents: write | |
| jobs: | |
| discover: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| repos: ${{ steps.list.outputs.repos }} | |
| steps: | |
| - name: List repos | |
| id: list | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if [ -n "${{ github.event.inputs.target_repo }}" ]; then | |
| echo 'repos=["${{ github.event.inputs.target_repo }}"]' >> $GITHUB_OUTPUT | |
| else | |
| REPOS=$(gh repo list SNAPKITTYWEST --limit 100 --json name --jq '[.[].name]') | |
| echo "repos=$REPOS" >> $GITHUB_OUTPUT | |
| fi | |
| agent: | |
| needs: discover | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| repo: ${{ fromJson(needs.discover.outputs.repos) }} | |
| max-parallel: 8 | |
| fail-fast: false | |
| steps: | |
| - name: Checkout target repo | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: SNAPKITTYWEST/${{ matrix.repo }} | |
| token: ${{ secrets.PAT_TOKEN }} | |
| path: repo | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install deps | |
| run: pip install requests orjson | |
| - name: Run agent on ${{ matrix.repo }} | |
| env: | |
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | |
| REPO_NAME: ${{ matrix.repo }} | |
| OVERRIDE_TASK: ${{ github.event.inputs.task }} | |
| WORKSPACE: ${{ github.workspace }}/repo | |
| run: python repo/.github/scripts/agent.py || python - << 'PYEOF' | |
| import os, sys, re, requests | |
| from pathlib import Path | |
| KEY = os.environ.get("OPENROUTER_API_KEY", "") | |
| ROOT = Path(os.environ.get("WORKSPACE", "repo")) | |
| REPO = os.environ.get("REPO_NAME", "unknown") | |
| TASK = os.environ.get("OVERRIDE_TASK", "") | |
| MODEL = "qwen/qwen-2.5-coder-32b-instruct" | |
| SCAN_PATTERNS = { | |
| "sorry": (["*.lean"], r'\bsorry\b'), | |
| "todo": (["*.py","*.rs","*.hs","*.ml","*.lean"], r'#\s*TODO|--\s*TODO|//\s*TODO'), | |
| "stub": (["*.py"], r'raise NotImplementedError'), | |
| "fixme": (["*.py","*.rs","*.lean"], r'FIXME|XXX'), | |
| } | |
| def or_call(prompt): | |
| if not KEY: return "" | |
| resp = requests.post("https://openrouter.ai/api/v1/chat/completions", | |
| headers={"Authorization": f"Bearer {KEY}", "HTTP-Referer": "https://snapkittywest.io"}, | |
| json={"model": MODEL, "messages": [{"role":"user","content":prompt}], | |
| "temperature": 0.1, "max_tokens": 8192}, timeout=300) | |
| return resp.json()["choices"][0]["message"]["content"] | |
| findings = [] | |
| for tag, (globs, pattern) in SCAN_PATTERNS.items(): | |
| for g in globs: | |
| for path in ROOT.rglob(g): | |
| if any(p in str(path) for p in [".github","node_modules",".git","__pycache__",".lake","build"]): | |
| continue | |
| try: | |
| text = path.read_text(encoding="utf-8", errors="ignore") | |
| for i, line in enumerate(text.split("\n")): | |
| if re.search(pattern, line): | |
| findings.append({"tag":tag,"file":str(path.relative_to(ROOT)),"line":i+1,"content":line.strip()}) | |
| except: pass | |
| print(f"[{REPO}] {len(findings)} findings") | |
| if not findings: | |
| print("Clean repo — skipping") | |
| sys.exit(0) | |
| priority = {"sorry":0,"stub":1,"todo":2,"fixme":3} | |
| findings.sort(key=lambda f: priority.get(f["tag"],9)) | |
| top = findings[:3] | |
| first = top[0] | |
| try: | |
| full = (ROOT / first["file"]).read_text(encoding="utf-8", errors="ignore") | |
| except: full = first["content"] | |
| items = "\n".join(f"[{f['tag'].upper()}] {f['file']}:{f['line']} — {f['content']}" for f in top) | |
| prompt = f"""Fix these issues in {REPO}/{first['file']}. Output ONLY the complete corrected file. No markdown. | |
| ISSUES: | |
| {items} | |
| FILE: | |
| {full[:6000]} | |
| """ | |
| result = or_call(TASK if TASK else prompt) | |
| if not result or len(result) < 50: | |
| print("Empty response"); sys.exit(0) | |
| m = re.search(r'```[\w]*\n(.*?)```', result, re.DOTALL) | |
| if m: result = m.group(1).strip() | |
| else: | |
| m2 = re.search(r'^(import |from |namespace |theorem |def |pub )', result, re.MULTILINE) | |
| if m2: result = result[m2.start():] | |
| target = ROOT / first["file"] | |
| target.write_text(result, encoding="utf-8") | |
| print(f"Written: {target} ({len(result)} chars)") | |
| PYEOF | |
| - name: Commit and push | |
| working-directory: repo | |
| run: | | |
| git config user.name "SNAPKITTY-SWARM" | |
| git config user.email "swarm@snapkittywest.io" | |
| git add -A | |
| if git diff --cached --quiet; then | |
| echo "[${{ matrix.repo }}] Nothing to commit." | |
| else | |
| git commit -m "⟦Ω⟧ swarm(${{ matrix.repo }}): $(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| git push origin HEAD | |
| echo "[${{ matrix.repo }}] Pushed." | |
| fi | |