From 7656fc0ed2aa9b86d9b930c690ce0e329e15d28d Mon Sep 17 00:00:00 2001 From: Jonathan Segev Date: Mon, 15 Jun 2026 11:44:43 -0400 Subject: [PATCH] ci: add /strands-ts command handler --- .github/workflows/strands-command.yml | 6 +- .github/workflows/strands-ts-command.yml | 98 ++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/strands-ts-command.yml diff --git a/.github/workflows/strands-command.yml b/.github/workflows/strands-command.yml index 00522ed6b8..8a94b4bd3d 100644 --- a/.github/workflows/strands-command.yml +++ b/.github/workflows/strands-command.yml @@ -22,7 +22,9 @@ on: jobs: authorization-check: - if: startsWith(github.event.comment.body, '/strands') || github.event_name == 'workflow_dispatch' + # Exclude '/strands-ts' so strands-ts-command.yml handles it (the '/strands' + # prefix would otherwise match and fire both workflows on one comment). + if: (startsWith(github.event.comment.body, '/strands') && !startsWith(github.event.comment.body, '/strands-ts')) || github.event_name == 'workflow_dispatch' name: Check access permissions: contents: read @@ -82,7 +84,7 @@ jobs: write_permission: 'false' finalize: - if: always() && (startsWith(github.event.comment.body, '/strands') || github.event_name == 'workflow_dispatch') + if: always() && ((startsWith(github.event.comment.body, '/strands') && !startsWith(github.event.comment.body, '/strands-ts')) || github.event_name == 'workflow_dispatch') needs: [setup-and-process, execute-readonly-agent] permissions: contents: write diff --git a/.github/workflows/strands-ts-command.yml b/.github/workflows/strands-ts-command.yml new file mode 100644 index 0000000000..f54db3acdc --- /dev/null +++ b/.github/workflows/strands-ts-command.yml @@ -0,0 +1,98 @@ +# Multi-agent TypeScript PR reviewer, triggered by `/strands-ts `. +# Mirrors strands-command.yml (the Python /strands handler) but invokes the +# strands-ts actions. Runs ALONGSIDE /strands; nothing is replaced. +# +# BLOCKED BY: strands-agents/devtools#68 — the strands-ts-* actions referenced +# below only exist on devtools@main AFTER that PR merges. Do not merge this +# until #68 is merged, or the runner/finalize steps will 404. +name: Strands-TS Command Handler + +on: + issue_comment: + types: [created] + +# No workflow-level write perms: the read/write split is enforced per job so +# the agent job never holds a write-capable token. +permissions: {} + +jobs: + authorization-check: + if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/strands-ts') }} + name: Check access + permissions: + contents: read + runs-on: ubuntu-latest + outputs: + approval-env: ${{ steps.auth.outputs.approval-env }} + steps: + - name: Check Authorization + id: auth + uses: strands-agents/devtools/authorization-check@main + with: + username: ${{ github.event.comment.user.login || 'invalid' }} + allowed-roles: 'maintain,triage,write,admin' + + mark-running: + needs: [authorization-check] + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Add strands-running label + env: + GH_TOKEN: ${{ github.token }} + PR_NUM: ${{ github.event.issue.number }} + run: gh pr edit "$PR_NUM" --repo "${{ github.repository }}" --add-label strands-running || true + + execute-readonly-agent: + needs: [authorization-check, mark-running] + environment: ${{ needs.authorization-check.outputs.approval-env }} + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + pull-requests: read + id-token: write # AWS OIDC role assumption only + steps: + - uses: actions/checkout@v4 + - name: Resolve PR head SHA + id: pr + env: + GH_TOKEN: ${{ github.token }} + PR_NUM: ${{ github.event.issue.number }} + run: echo "sha=$(gh pr view "$PR_NUM" --json headRefOid -q .headRefOid)" >> "$GITHUB_OUTPUT" + - name: Run Strands-TS Agent + uses: strands-agents/devtools/strands-command/actions/strands-ts-runner@main + with: + command: ${{ github.event.comment.body }} + pr_number: ${{ github.event.issue.number }} + pr_head_sha: ${{ steps.pr.outputs.sha }} + aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} + agents_config: ${{ vars.STRANDS_TS_AGENTS || '' }} + + finalize: + needs: [execute-readonly-agent] + if: ${{ needs.execute-readonly-agent.result == 'success' }} + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write # the ONLY job that can write; replays vetted artifact ops + steps: + - uses: actions/checkout@v4 + - name: Replay deferred writes + uses: strands-agents/devtools/strands-command/actions/strands-ts-finalize@main + + clear-running: + needs: [mark-running, execute-readonly-agent, finalize] + if: ${{ always() && needs.mark-running.result == 'success' }} + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Remove strands-running label + env: + GH_TOKEN: ${{ github.token }} + PR_NUM: ${{ github.event.issue.number }} + run: gh pr edit "$PR_NUM" --repo "${{ github.repository }}" --remove-label strands-running || true