feat(init): add OpenCode tool support #25
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: AI PR Review | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| models: read | |
| jobs: | |
| review: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| # Checkout base repo (not PR head) for safe access to CLAUDE.md and proposals. | |
| # The diff is fetched via GitHub API, not from the PR code. | |
| ref: ${{ github.event.pull_request.base.sha }} | |
| fetch-depth: 1 | |
| - name: Get PR diff | |
| id: diff | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh pr diff ${{ github.event.pull_request.number }} > /tmp/pr-full.diff | |
| FULL_SIZE=$(wc -c < /tmp/pr-full.diff) | |
| echo "diff_size=$FULL_SIZE" >> "$GITHUB_OUTPUT" | |
| # Truncate to ~80k chars to stay within API limits | |
| if [ "$FULL_SIZE" -gt 80000 ]; then | |
| head -c 80000 /tmp/pr-full.diff > /tmp/pr.diff | |
| echo "truncated=true" >> "$GITHUB_OUTPUT" | |
| else | |
| cp /tmp/pr-full.diff /tmp/pr.diff | |
| echo "truncated=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Get PR info | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh pr view ${{ github.event.pull_request.number }} --json title,body > /tmp/pr-info.json | |
| - name: Gather project context | |
| run: | | |
| # Read CLAUDE.md summary (first 100 lines) | |
| if [ -f "CLAUDE.md" ]; then | |
| head -100 CLAUDE.md > /tmp/project-context.txt | |
| else | |
| echo "No CLAUDE.md found." > /tmp/project-context.txt | |
| fi | |
| # List proposals | |
| if [ -d "docs/03-development/proposals" ]; then | |
| ls docs/03-development/proposals/ 2>/dev/null | head -20 > /tmp/proposals.txt | |
| else | |
| echo "No proposals directory." > /tmp/proposals.txt | |
| fi | |
| - name: Build API request | |
| env: | |
| TRUNCATED: ${{ steps.diff.outputs.truncated }} | |
| DIFF_SIZE: ${{ steps.diff.outputs.diff_size }} | |
| run: | | |
| TRUNCATION_NOTE="" | |
| if [ "$TRUNCATED" = "true" ]; then | |
| TRUNCATION_NOTE="NOTE: The diff was truncated to 80k characters. Total size: ${DIFF_SIZE} bytes. Focus on what is visible." | |
| fi | |
| # Write the user prompt to a file (avoids shell variable limits) | |
| cat > /tmp/user-prompt.txt <<PROMPT | |
| Review this pull request. | |
| PR Info: | |
| $(cat /tmp/pr-info.json) | |
| Project context: | |
| $(cat /tmp/project-context.txt) | |
| Proposals in repo: | |
| $(cat /tmp/proposals.txt) | |
| ${TRUNCATION_NOTE} | |
| Focus on: | |
| 1. Bugs, logic errors, race conditions, resource leaks | |
| 2. Error handling gaps | |
| 3. Security concerns (command injection, temp files, predictable paths) | |
| 4. Scope — flag if PR bundles unrelated changes | |
| 5. If proposals exist that match the PR topic, check alignment | |
| 6. Check if PR claims to fix already-fixed issues | |
| Structure your review as: Critical Issues, Moderate Issues, Minor Issues, What Looks Good, Recommendation (approve/request changes). | |
| Diff: | |
| $(cat /tmp/pr.diff) | |
| PROMPT | |
| # Build JSON payload using jq with file input (no shell variable limits) | |
| jq -n --rawfile prompt /tmp/user-prompt.txt '{ | |
| model: "openai/gpt-4.1", | |
| messages: [ | |
| { | |
| role: "system", | |
| content: "You are a code reviewer for a Go CLI project (mxcli) that reads/modifies Mendix application projects. Key patterns: ANTLR4 grammar → AST → visitor → executor → BSON writer. Generated ANTLR parser files (mdl/grammar/parser/) are noise — note but skip. Review thoroughly but concisely." | |
| }, | |
| { | |
| role: "user", | |
| content: $prompt | |
| } | |
| ], | |
| max_tokens: 4000 | |
| }' > /tmp/request.json | |
| echo "Request payload size: $(wc -c < /tmp/request.json) bytes" | |
| - name: Call GitHub Models API | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| HTTP_CODE=$(curl -s -w "%{http_code}" -o /tmp/response.json -X POST \ | |
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d @/tmp/request.json \ | |
| https://models.github.ai/inference/chat/completions) | |
| echo "HTTP status: $HTTP_CODE" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::warning::GitHub Models API returned HTTP $HTTP_CODE" | |
| cat /tmp/response.json | head -c 1000 | |
| exit 0 | |
| fi | |
| REVIEW=$(jq -r '.choices[0].message.content // empty' /tmp/response.json) | |
| if [ -z "$REVIEW" ]; then | |
| echo "::warning::AI review returned empty content. Response:" | |
| cat /tmp/response.json | head -c 1000 | |
| exit 0 | |
| fi | |
| # Save review for next step | |
| echo "$REVIEW" > /tmp/review.txt | |
| echo "Review generated ($(wc -c < /tmp/review.txt) bytes)" | |
| - name: Post review comment | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| if [ ! -f /tmp/review.txt ]; then | |
| echo "No review to post." | |
| exit 0 | |
| fi | |
| # Build comment body | |
| { | |
| echo "## AI Code Review" | |
| echo "" | |
| cat /tmp/review.txt | |
| echo "" | |
| echo "---" | |
| echo "*Automated review by GitHub Models API (GPT-4.1) — [workflow source](${{ github.server_url }}/${{ github.repository }}/blob/main/.github/workflows/ai-review.yml)*" | |
| } > /tmp/comment.md | |
| gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/comment.md |