Skip to content

Runtime configuration support / MDC support #4

Runtime configuration support / MDC support

Runtime configuration support / MDC support #4

Workflow file for this run

name: Issue updated → dispatch to coding agent
on:
issues:
types:
- opened
- edited
- reopened
- assigned
- unassigned
permissions:
contents: read
issues: read
# Optional: set in Settings → Variables → Repository variables
# CODING_AGENT_USER = <agent-gh-handle> (leave empty to disable the assignee filter)
env:
CODING_AGENT_USER: ${{ vars.CODING_AGENT_USER }}
concurrency:
group: issue-${{ github.event.issue.number }}-dispatch
cancel-in-progress: true
jobs:
dispatch:
# Only members/maintainers/collaborators can trigger runs from a public repo
if: ${{ contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.actor_association) }}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Determine issue type and assignment (Issue Types only)
id: meta
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # gh uses GH_TOKEN
CODING_AGENT_USER: ${{ env.CODING_AGENT_USER }}
run: |
set -euo pipefail
OWNER="${GITHUB_REPOSITORY%/*}"
REPO="${GITHUB_REPOSITORY#*/}"
NUMBER="${{ github.event.issue.number }}"
# Query: issue type + assignees (no labels)
RESP="$(gh api graphql \
-H 'GraphQL-Features: issue_types' \
-f owner="$OWNER" \
-f repo="$REPO" \
-F number="$NUMBER" \
-f query='
query($owner:String!, $repo:String!, $number:Int!) {
repository(owner:$owner, name:$repo) {
issue(number:$number) {
number
title
issueType { name }
assignees(first: 100) { nodes { login } }
}
}
}'
)"
TYPE="$(jq -r '.data.repository.issue.issueType.name // empty' <<< "$RESP")"
# Optional assignee filter
RAW_AGENT="${CODING_AGENT_USER:-}"
AGENT_CLEAN="$(tr -d '\n' <<< "${RAW_AGENT#@}" | tr '[:upper:]' '[:lower:]')"
if [[ -z "$AGENT_CLEAN" ]]; then
ASSIGNED_OK="true"
else
ASSIGNED_OK="$(
jq -r --arg agent "$AGENT_CLEAN" '
[.data.repository.issue.assignees.nodes[].login // empty
| ascii_downcase] | index($agent) | if .==null then "false" else "true" end
' <<< "$RESP"
)"
fi
# Type filter: only "Task" or "Bug"
case "$(tr '[:upper:]' '[:lower:]' <<< "${TYPE}")" in
bug|task) TYPE_OK="true" ;;
*) TYPE_OK="false" ;;
esac
SHOULD=$([[ "$TYPE_OK" == "true" && "$ASSIGNED_OK" == "true" ]] && echo true || echo false)
{
echo "issue_type=${TYPE}"
echo "assigned_ok=${ASSIGNED_OK}"
echo "type_ok=${TYPE_OK}"
echo "should_dispatch=${SHOULD}"
echo "agent_user=${AGENT_CLEAN}"
} >> "$GITHUB_OUTPUT"
- name: Log & skip if not eligible
if: ${{ steps.meta.outputs.should_dispatch != 'true' }}
run: |
echo "Issue #${{ github.event.issue.number }} not eligible for dispatch."
echo " issue_type='${{ steps.meta.outputs.issue_type }}' (type_ok=${{ steps.meta.outputs.type_ok }})"
echo " assigned_ok=${{ steps.meta.outputs.assigned_ok }} agent='${{ steps.meta.outputs.agent_user }}'"
echo " Note: This workflow requires GitHub Issue Types ('Task' or 'Bug')."
- name: Build payload
if: ${{ steps.meta.outputs.should_dispatch == 'true' }}
id: payload
env:
ISSUE_TYPE: ${{ steps.meta.outputs.issue_type }}
AGENT_USER: ${{ steps.meta.outputs.agent_user }}
ASSIGNED_OK: ${{ steps.meta.outputs.assigned_ok }}
run: |
jq -n \
--arg repo "${{ github.repository }}" \
--argjson issue ${{ github.event.issue.number }} \
--arg url "${{ github.event.issue.html_url }}" \
--arg title "${{ github.event.issue.title }}" \
--arg actor "${{ github.actor }}" \
--arg action "${{ github.event.action }}" \
--arg issue_type "${ISSUE_TYPE:-}" \
--arg agent_user "${AGENT_USER:-}" \
--arg assigned_ok "${ASSIGNED_OK:-}" \
'{event_type:"coding_agent_dispatch",
client_payload:{
repo:$repo,
issue:$issue,
issue_html_url:$url,
issue_title:$title,
issue_actor:$actor,
issue_action:$action,
issue_type:$issue_type,
agent_user:$agent_user,
assigned_ok:$assigned_ok
}}' \
> payload.json
- name: Preflight (token & target repo access)
if: ${{ steps.meta.outputs.should_dispatch == 'true' }}
env:
GH_TOKEN: ${{ secrets.AIBUDDY_DISPATCH_PAT }}
run: |
set -euo pipefail
TARGET="hyperifyio/aibuddy"
if [[ -z "${GH_TOKEN:-}" ]]; then
echo "::error title=Missing secret::AIBUDDY_DISPATCH_PAT is not set."
exit 1
fi
# Verify token can access the private repo
if ! gh api "repos/$TARGET" >/dev/null 2>err.txt; then
echo "::error title=Token cannot access target repo::$TARGET not accessible with provided token."
cat err.txt || true
exit 1
fi
# Validate payload JSON
jq -e . payload.json >/dev/null || {
echo "::error title=Invalid payload::payload.json is not valid JSON"
cat payload.json
exit 1
}
- name: Send repository_dispatch to aibuddy (gh api)
if: ${{ steps.meta.outputs.should_dispatch == 'true' }}
env:
GH_TOKEN: ${{ secrets.AIBUDDY_DISPATCH_PAT }}
run: |
set -euo pipefail
gh api repos/hyperifyio/aibuddy/dispatches \
--method POST \
-H "Accept: application/vnd.github+json" \
--input payload.json
echo "repository_dispatch sent for issue #${{ github.event.issue.number }} (type=${{ steps.meta.outputs.issue_type }}, agent='${{ steps.meta.outputs.agent_user }}')"