-
Notifications
You must be signed in to change notification settings - Fork 27
234 lines (195 loc) · 10.6 KB
/
ai-review.yml
File metadata and controls
234 lines (195 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
name: AI PR Review
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
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 full CLAUDE.md (contains architecture, review checklist, key concepts)
if [ -f "CLAUDE.md" ]; then
cat 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
# List existing doctype test files for overlap checking
if [ -d "mdl-examples/doctype-tests" ]; then
ls mdl-examples/doctype-tests/ 2>/dev/null | head -30 > /tmp/doctype-tests.txt
else
echo "No doctype-tests directory." > /tmp/doctype-tests.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 (CLAUDE.md — contains architecture + review checklist):
$(cat /tmp/project-context.txt)
Proposals in repo:
$(cat /tmp/proposals.txt)
Existing doctype test files:
$(cat /tmp/doctype-tests.txt)
${TRUNCATION_NOTE}
Review against ALL of the following checklist items:
## Overlap & duplication
- Check proposals list above for existing proposals covering the same functionality
- Check if the diff introduces code that duplicates existing implementations (look for similar function names, command names, types)
- Check doctype test files above for existing test coverage of the feature area
- Flag if the PR re-documents already-shipped features as new
## MDL syntax design (for new or modified MDL syntax)
If the diff adds or modifies MDL syntax (grammar rules, keywords, statement types), check:
- Follows standard CREATE/ALTER/DROP/SHOW/DESCRIBE pattern — no custom verbs for CRUD operations
- Uses Module.Element qualified names everywhere — no bare names, no implicit module context
- Property lists use ( Key: value, ... ) format with colon separators, one per line
- Colon for property definitions (Key: value), AS for name-to-name mappings ('oldName' AS 'newName') — do not flag AS in mapping/renaming contexts as wrong
- Keywords are full English words — no abbreviations, no symbols for domain operations
- Statement reads as English — a business analyst can understand the intent
- No keyword overloading — each keyword has one consistent meaning
- Diff-friendly — adding one property produces a one-line diff
- Consistent with existing MDL patterns (check docs/01-project/MDL_QUICK_REFERENCE.md)
## Full-stack consistency (for MDL features)
New MDL commands or language features must be wired through the full pipeline. If the diff adds any MDL feature, check:
- Grammar rule added to MDLParser.g4 (and MDLLexer.g4 if new tokens)
- AST node type added in mdl/ast/
- Visitor handler in mdl/visitor/
- Executor handler in mdl/executor/
- LSP wiring (cmd/mxcli/lsp.go) if the feature adds formatting, diagnostics, or navigation
- DESCRIBE roundtrip — if the feature creates artifacts, DESCRIBE should output re-executable MDL
- VS Code extension (vscode-mdl/package.json) updated if new LSP capabilities added
## Test coverage
- New packages should have test files
- New executor commands should have MDL examples in mdl-examples/doctype-tests/
- Integration paths should be tested, not just helpers
- No time.Sleep for synchronization — use channels or polling with timeout
## Security & robustness
- Command injection, XSS, SQL injection, temp file issues
- Unix sockets should use restrictive permissions (0600)
- File I/O should not be in hot paths (event loops, per-keystroke handlers)
- No silent side effects on typos (e.g., auto-creating resources on misspelled names)
- Correct method receivers (pointer vs value) for mutations
## Scope & atomicity
- Each commit should do one thing — a feature, a bugfix, or a refactor, not a mix
- PR should be scoped to a single feature or concern — flag if description needs "and" between unrelated items
- Independent features should be in separate PRs
- Refactors touching many files should be their own commit
## Code quality
- Refactors should be applied consistently (look for old patterns still present)
- Manually maintained lists (keyword lists, type mappings) should be flagged as maintenance risks
- Design docs should match the actual implementation
## Bugs & correctness
- Logic errors, race conditions, resource leaks
- Error handling gaps
Structure your review as: Critical Issues, Moderate Issues, Minor Issues, What Looks Good, Recommendation (approve/request changes).
Only list sections that have findings — skip empty sections.
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: "nvidia/nemotron-3-super-120b-a12b:free",
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. New MDL features MUST be wired through the full pipeline (grammar → AST → visitor → executor → LSP → DESCRIBE roundtrip). New MDL syntax must follow design guidelines: reads as English (target audience is citizen developers), uses standard CRUD verbs (CREATE/ALTER/DROP/SHOW/DESCRIBE), consistent property format (Key: value) for property definitions, AS for name-to-name mappings ('old' AS 'new'), qualified names (Module.Element), no keyword overloading, no symbolic syntax. IMPORTANT: colon is for property definitions (Key: value), AS is for name mappings/renaming — do not flag AS in mapping contexts as a syntax violation. Generated ANTLR parser files (mdl/grammar/parser/) are noise — note but skip. The project context includes the full CLAUDE.md with architecture details and the review checklist. Review against the checklist 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 OpenRouter API
env:
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
run: |
if [ -z "$OPENROUTER_API_KEY" ]; then
echo "::warning::OPENROUTER_API_KEY secret is not set"
exit 0
fi
HTTP_CODE=$(curl -s -w "%{http_code}" -o /tmp/response.json -X POST \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/request.json \
https://openrouter.ai/api/v1/chat/completions)
echo "HTTP status: $HTTP_CODE"
if [ "$HTTP_CODE" != "200" ]; then
echo "::warning::OpenRouter 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 via OpenRouter (Nemotron Super 120B) — [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