#!/usr/bin/env bash # Route agent + prepare branch. # Reads the event context from env (set by the calling step), writes the agent registry to # /tmp/agents.json, picks which agent to run, emits step outputs (name/model/vision/mode/branch/new) # to $GITHUB_OUTPUT, configures git identity, and prepares/publishes the working branch. # # Required env (all provided by the workflow step): BODY IBODY CID IS_PR NUM GT # TOKEN_PM TOKEN_SENIOR TOKEN_JUNIOR TOKEN_LEAD TOKEN_QA # GITHUB_SERVER_URL GITHUB_REPOSITORY GITHUB_OUTPUT set -eu # --- agent registry: model + capabilities + mode + role + skills --- # `skills` is the allow-list of opencode Skills each agent may load. It scopes the # `permission.skill` block written into opencode.json (see install-opencode.sh) so an agent only # ever sees (and can load) the skills relevant to its role. Skills NOT listed here are hidden from # that agent entirely — not even the one-line summary appears in its , so the # full API/how-to detail never reaches an agent that shouldn't act on it. A teammate can still learn # *that* another agent has a capability from the roster and ask them to use it. cat > /tmp/agents.json <<'JSON' { "pm": {"model":"ollama-cloud/gemma4:cloud","vision":true, "mode":"comment","skills":["gitea-api"],"desc":"Product manager — research, plan, ask clarifying questions, and decide which dev should do the work. Comments only; never edits files."}, "junior": {"model":"ollama-cloud/kimi-k2.7-code:cloud","vision":false,"mode":"pr", "skills":[],"desc":"Junior dev — small, low-risk changes (mostly YAML/compose/config). Text-only, cannot read images. Defers complex or image tasks to @senior or @lead."}, "senior": {"model":"ollama-cloud/glm-5.2:cloud","vision":false,"mode":"pr", "skills":["gitea-api","node1-ssh"],"desc":"Senior dev — complex, multi-file implementation (GLM-5.2 via Ollama Cloud, text-only)."}, "lead": {"model":"anthropic/claude-opus-4-8","vision":true, "mode":"pr", "skills":["gitea-api","node1-ssh"],"desc":"Tech lead — the hardest problems, architecture, and final calls."}, "qa": {"model":"ollama-cloud/minimax-m3:cloud","vision":true, "mode":"comment","skills":["gitea-api"],"desc":"QA — verifies things work. Drives a headless browser (Playwright) to open a URL/web app, click through it, screenshot, and report bugs or confirm behavior. Comments findings; opens no PRs."} } JSON # On a new issue, @pm auto-assesses. On a comment, route by the @mention. # A comment event has a comment id (CID); an issue-opened event does not. (event_name is unreliable # here — see agent.yml: this reusable workflow sees it as 'workflow_call'.) if [ -n "$CID" ]; then scan="$BODY"; else scan="$IBODY"; fi name="" for a in pm junior senior lead qa; do case "$scan" in *"@$a"*) name=$a; break;; esac done if [ -z "$name" ]; then if [ -z "$CID" ]; then name=pm; else echo "no known agent mentioned"; exit 1; fi fi model=$(jq -r --arg a "$name" '.[$a].model' /tmp/agents.json) vision=$(jq -r --arg a "$name" '.[$a].vision' /tmp/agents.json) mode=$(jq -r --arg a "$name" '.[$a].mode' /tmp/agents.json) # Compact JSON array of the skills this agent may load (scopes permission.skill in install-opencode.sh). skills=$(jq -c --arg a "$name" '.[$a].skills // []' /tmp/agents.json) echo "Routing to @$name (model=$model vision=$vision mode=$mode skills=$skills)" { echo "name=$name"; echo "model=$model"; echo "vision=$vision"; echo "mode=$mode"; echo "skills=$skills"; } >> "$GITHUB_OUTPUT" # Publish the full agent registry so install-opencode.sh can derive the provider `models:` map # from it — single source of truth for Ollama Cloud model IDs, so the two files can't drift. echo "agents=$(jq -c '.' /tmp/agents.json)" >> "$GITHUB_OUTPUT" # Act as the agent's own Gitea user when its token is set; else the built-in bot. case "$name" in pm) TOK="$TOKEN_PM";; senior) TOK="$TOKEN_SENIOR";; junior) TOK="$TOKEN_JUNIOR";; lead) TOK="$TOKEN_LEAD";; qa) TOK="$TOKEN_QA";; *) TOK="";; esac [ -z "$TOK" ] && TOK="$GT" git config user.name "$name" git config user.email "$name@ffaerber.duckdns.org" API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" hdr=(-H "Authorization: token $TOK" -H "Content-Type: application/json") if [ -n "$IS_PR" ]; then # comment on a PR -> resume its branch ref=$(curl -s -H "Authorization: token $GT" "$API/pulls/$NUM" | jq -r .head.ref) git fetch origin "$ref" && git checkout "$ref" { echo "branch=$ref"; echo "new=false"; } >> "$GITHUB_OUTPUT" else # comment on an issue -> new branch git checkout -b "ai/issue-$NUM" { echo "branch=ai/issue-$NUM"; echo "new=true"; } >> "$GITHUB_OUTPUT" # For dev agents, publish the branch immediately and tell the maintainer where to watch. if [ "$mode" = "pr" ]; then git push -u origin "HEAD:ai/issue-$NUM" || true url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/ai/issue-$NUM" curl -sS -X POST "${hdr[@]}" "$API/issues/$NUM/comments" \ -d "$(jq -nc --arg b "🔨 **@$name** is on it — building on branch [\`ai/issue-$NUM\`]($url). I'll open a PR when it's ready." '{body:$b}')" >/dev/null || true fi fi