Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions .github/workflows/agent-review-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
name: Agent / Review PR

on:
pull_request:
types: [opened, synchronize]
branches: [main, development]

workflow_dispatch:
inputs:
pr_number:
description: "PR number to review"
required: true
type: string
base_ref:
description: "Base branch of the PR (e.g. development)"
required: true
type: string
head_ref:
description: "Head branch of the PR"
required: true
type: string

permissions:
contents: read
pull-requests: write
issues: write
id-token: write

jobs:
tests:
if: github.base_ref == 'development' || (github.event_name == 'workflow_dispatch' && inputs.base_ref == 'development')
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
node-version: [20.x, 22.x]

steps:
- uses: actions/checkout@v4

- name: Checkout PR (workflow_dispatch)
if: github.event_name == 'workflow_dispatch'
run: gh pr checkout ${{ inputs.pr_number }}
env:
GH_TOKEN: ${{ github.token }}

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Verify (format, typecheck, test)
run: npm run verify

review:
if: (github.base_ref == 'development' && github.event.action == 'opened') || (github.event_name == 'workflow_dispatch' && inputs.base_ref == 'development')
runs-on: ubuntu-latest
timeout-minutes: 15
outputs:
verdict: ${{ steps.verdict.outputs.verdict }}

steps:
- name: Generate review bot token
id: review-bot-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.LLM_EXE_REVIEW_BOT_APP_ID }}
private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }}

- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.review-bot-token.outputs.token }}

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Review PR
env:
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
HEAD_REF: ${{ github.event.pull_request.head.ref || inputs.head_ref }}
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ steps.review-bot-token.outputs.token }}
display_report: "false"
allowed_bots: "llm-exe-bot[bot]"
prompt: |
You are a senior engineer reviewing PR #${{ env.PR_NUMBER }} on the llm-exe GitHub Action repository.

This repo is a GitHub Action wrapper around the llm-exe SDK. It runs committed dist/index.js
(not src/) so every code change must be accompanied by a rebuilt dist/. The action exposes
inputs/outputs defined in action.yml and is consumed by other GitHub workflows via
`uses: llm-exe/github-action@v1`.

## Your task

1. Read CLAUDE.md for project context.
2. Fetch the PR diff: `gh pr diff ${{ env.PR_NUMBER }}`
3. Read any changed source files in full.
4. Check that dist/ was rebuilt if src/ changed: look for dist/ changes alongside src/ changes.
5. Review for: correctness, security, type safety, action.yml contract changes, and dist freshness.

## Verdict

After your review, post a PR comment with your findings. Then write your verdict to
/tmp/review-verdict.txt — one word only:
- `approve` — code is correct, dist is fresh (if src changed), no blocking issues
- `request-changes` — there are issues that must be fixed before merging
- `comment` — minor notes only, no blocking issues but not ready to formally approve

If the branch name starts with `agent/` note that this is bot-authored code — hold it
to the same standards but expect mechanical patterns.
claude_args: |
--allowedTools "Bash,Read,Glob,Grep,WebFetch"
--max-turns 30
--model ${{ vars.ANTHROPIC_OPUS_LATEST || 'claude-opus-4-6' }}

- name: Read verdict
id: verdict
if: always()
run: |
if [ -f /tmp/review-verdict.txt ]; then
v=$(cat /tmp/review-verdict.txt | tr -d '[:space:]')
else
v="no-verdict"
fi
echo "verdict=$v" >> "$GITHUB_OUTPUT"

decide:
needs: [tests, review]
if: always() && (github.base_ref == 'development' || (github.event_name == 'workflow_dispatch' && inputs.base_ref == 'development'))
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Generate review bot token
id: review-bot-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.LLM_EXE_REVIEW_BOT_APP_ID }}
private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }}

- name: Generate bot token
id: bot-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Approve or skip
env:
GH_TOKEN: ${{ steps.review-bot-token.outputs.token }}
BOT_TOKEN: ${{ steps.bot-token.outputs.token }}
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
HEAD_REF: ${{ github.event.pull_request.head.ref || inputs.head_ref }}
REPOSITORY: ${{ github.repository }}
run: |
verdict="${{ needs.review.outputs.verdict }}"
tests_result="${{ needs.tests.result }}"

echo "Review verdict : $verdict"
echo "Tests result : $tests_result"

if [ "$verdict" = "approve" ] && [ "$tests_result" = "success" ]; then
gh pr review "$PR_NUMBER" \
--approve \
--body "Approved by reviewer agent (tests passing)." \
--repo "$REPOSITORY"

if echo "$HEAD_REF" | grep -q '^agent/'; then
IS_DRAFT=$(gh pr view "$PR_NUMBER" \
--json isDraft --jq '.isDraft' \
--repo "$REPOSITORY")
if [ "$IS_DRAFT" = "true" ]; then
GH_TOKEN="$BOT_TOKEN" gh pr ready "$PR_NUMBER" --repo "$REPOSITORY"
echo "PR marked as ready for review."
fi
fi

echo "PR #$PR_NUMBER approved."
else
echo "No approval submitted: verdict='$verdict', tests='$tests_result'."
fi
88 changes: 88 additions & 0 deletions .github/workflows/auto-merge-main-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Release / Auto Merge

on:
workflow_run:
workflows:
- "Release / Check Semver"
types:
- completed
pull_request:
types:
- ready_for_review
- synchronize
branches:
- main

permissions:
id-token: write
checks: write
contents: write
pull-requests: write
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
auto-merge:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'development' || github.event_name == 'pull_request' }}

steps:
- uses: actions/checkout@v4

- name: Generate bot token
id: bot-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Get PR number for development to main
run: |
PR_INFO=$(gh pr list --base main --head development --state open --json number,isDraft --jq '.[] | select(.isDraft == false) | .number')
echo "PR_NUMBER=$PR_INFO" >> $GITHUB_ENV
env:
GH_TOKEN: ${{ steps.bot-token.outputs.token }}
GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }}

- name: Wait for all checks to complete
if: env.PR_NUMBER != ''
run: |
MAX_ATTEMPTS=10
ATTEMPT=0
while [[ $ATTEMPT -lt $MAX_ATTEMPTS ]]; do
CHECKS_IN_PROGRESS=$(gh pr checks ${{ env.PR_NUMBER }} --json name,state --jq '[.[] | select(.name != "auto-merge") | select(.state == "IN_PROGRESS")] | length')
if [[ "$CHECKS_IN_PROGRESS" -eq "0" ]]; then
echo "All checks complete."
break
fi
echo "Checks still in progress... attempt $ATTEMPT/$MAX_ATTEMPTS"
ATTEMPT=$((ATTEMPT + 1))
sleep 30
done
env:
GH_TOKEN: ${{ steps.bot-token.outputs.token }}
GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }}

- name: Verify all checks passed
if: env.PR_NUMBER != ''
run: |
FAILED_CHECKS=$(gh pr checks ${{ env.PR_NUMBER }} --json name,state --jq '[.[] | select(.name != "auto-merge") | select(.state == "FAILURE")] | length')
if [[ "$FAILED_CHECKS" -gt "0" ]]; then
echo "Some checks failed — aborting auto-merge."
exit 1
fi
echo "All checks passed."
env:
GH_TOKEN: ${{ steps.bot-token.outputs.token }}
GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }}

- name: Merge PR to main
if: env.PR_NUMBER != ''
run: |
gh pr merge ${{ env.PR_NUMBER }} --merge --admin --repo ${{ github.repository }}
env:
GH_TOKEN: ${{ steps.bot-token.outputs.token }}
GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }}
114 changes: 114 additions & 0 deletions .github/workflows/bot-respond.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Agent / Bot Respond

on:
issue_comment:
types: [created]

permissions:
contents: write
issues: write
pull-requests: write
id-token: write

jobs:
respond:
if: |
contains(github.event.comment.body, '@llm-exe-bot') &&
github.event.comment.user.login != 'llm-exe-bot[bot]' &&
(
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'
)
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Generate bot token
id: bot-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Configure git
run: |
git config --global user.name "llm-exe-bot[bot]"
git config --global user.email "${{ secrets.APP_ID }}+llm-exe-bot[bot]@users.noreply.github.com"

- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.bot-token.outputs.token }}

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Respond
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ steps.bot-token.outputs.token }}
prompt: |
You are llm-exe-bot, a helpful assistant for the llm-exe GitHub Action repository.
You've been mentioned in a GitHub issue or PR comment by a maintainer or collaborator.

Read CLAUDE.md for project context. This repo is a GitHub Action wrapper around the
llm-exe SDK. It runs committed dist/index.js (not src/) so code changes must include
a rebuilt dist/.

## Determine what's being asked

Read the comment that mentioned you carefully. Decide which of the following applies:

### 1. PR review requested
If the maintainer wants you to review a pull request — any phrasing like "review this",
"re-review", "take another look", "check the PR", "can you review", etc. — AND the
comment is on a pull request:
- Fetch the PR's base and head branch:
`gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json baseRefName,headRefName`
- Dispatch the review pipeline:
`gh workflow run agent-review-pr.yml \
--repo ${{ github.repository }} \
-f pr_number="${{ github.event.issue.number }}" \
-f base_ref="<base_ref from above>" \
-f head_ref="<head_ref from above>"`
- Post a brief acknowledgment, e.g.:
"Review pipeline started — tests + agent review + approval will run shortly."
- Stop here. Do not also do a manual review.

### 2. Answer questions (read-only)
If the maintainer is asking a question or wants your opinion on something:
- Read any relevant source code
- Use `gh pr diff` or `gh pr view` to understand PR context
- Run `npm run verify` if needed to check the current state
- Reply with a concise, specific answer

### 3. Make changes (write mode)
If the maintainer is asking you to fix something, revise a PR, address review feedback,
or make code changes:
- If you're on a PR, check out the PR branch: `gh pr checkout <number>`
- Read the PR diff and any feedback
- Make the requested changes
- If you changed src/, rebuild dist/: `npm run build`
- Run `npm run verify` — everything must pass
- Commit with a descriptive message (do NOT add Co-Authored-By lines)
- Push to the existing PR branch
- Reply with a summary of what you changed

## Rules
- ONLY make changes when the maintainer explicitly asks you to
- If the request is ambiguous, ask for clarification — don't guess
- Stay scoped to what's asked. Don't refactor unrelated things.
- Do NOT create new PRs or branches — work on the existing PR branch
- Do NOT close issues or PRs unless explicitly told to
- Be conversational and concise. Reference actual code (file paths, line numbers) when relevant.
claude_args: |
--allowedTools "Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch"
--max-turns 90
--model ${{ vars.ANTHROPIC_OPUS_LATEST || 'claude-opus-4-6' }}
Loading
Loading