@@ -164,8 +164,37 @@ jobs:
164164 DIFF_STATS : ${{ steps.generate_diff.outputs.diff_stats }}
165165 IS_TRUNCATED : ${{ steps.generate_diff.outputs.is_truncated }}
166166 run : |
167+ # Create system context first (no variable expansion needed)
168+ cat > /tmp/review_prompt.txt << 'PROMPT_EOF'
169+ You are an expert C++ code reviewer for the Velox project.
170+
171+ **IMPORTANT**: First, read the CLAUDE.md file in the repository root. It contains:
172+ - Project overview and architecture
173+ - Build commands and environment setup
174+ - Code style and naming conventions
175+ - PR title format requirements
176+ - Testing guidelines
177+ - Function addition requirements
178+
179+ Use CLAUDE.md as your primary reference for project-specific standards.
180+
181+ Key things to know about Velox:
182+ - Uses C++20 standard
183+ - Heavy use of templates and SFINAE
184+ - Custom memory management with MemoryPool
185+ - Vectorized execution with custom Vector types
186+ - Follows Google C++ style with some modifications
187+
188+ Provide actionable, specific feedback. Reference exact file paths and line numbers.
189+ Be constructive and educational in your feedback.
190+ If the diff looks good, say so - don't invent problems.
191+
192+ ---
193+
194+ PROMPT_EOF
195+
167196 # Note: Using unquoted PROMPT_EOF to enable variable expansion
168- cat > /tmp/review_prompt.txt << PROMPT_EOF
197+ cat >> /tmp/review_prompt.txt << PROMPT_EOF
169198 Please review the following pull request for the Velox project.
170199
171200 ## Pull Request Information
@@ -233,24 +262,11 @@ jobs:
233262 uses : anthropics/claude-code-base-action@e8132bc5e637a42c27763fc757faa37e1ee43b34 # beta
234263 with :
235264 prompt_file : /tmp/review_prompt.txt
236- system_prompt : |
237- You are an expert C++ code reviewer for the Velox project.
238- Velox is a composable C++ execution engine library for analytical data processing.
239-
240- Key things to know about Velox:
241- - Uses C++20 standard
242- - Heavy use of templates and SFINAE
243- - Custom memory management with MemoryPool
244- - Vectorized execution with custom Vector types
245- - Follows Google C++ style with some modifications
246-
247- Provide actionable, specific feedback. Reference exact file paths and line numbers.
248- Be constructive and educational in your feedback.
249- If the diff looks good, say so - don't invent problems.
250-
251- # Read-only tools only - no Bash, no Edit, no Write
252- allowed_tools : View,GlobTool,GrepTool
253- max_turns : " 15"
265+ # Use Sonnet 4.6 for code review, with read-only tools and limited turns
266+ claude_args : >-
267+ --model claude-sonnet-4-6-20250514
268+ --max-turns 15
269+ --allowedTools View,GlobTool,GrepTool
254270 anthropic_api_key : ${{ secrets.ANTHROPIC_API_KEY }}
255271
256272 # Step 7: Post review as PR comment (skip if dry_run)
@@ -270,21 +286,66 @@ jobs:
270286
271287 try {
272288 // Read the execution log
273- const executionLog = JSON.parse(fs.readFileSync(process.env.EXECUTION_FILE, 'utf8'));
274-
275- // Extract Claude's final response
276- const assistantMessages = executionLog.messages.filter(m => m.role === 'assistant');
277- if (assistantMessages.length > 0) {
278- const lastMessage = assistantMessages[assistantMessages.length - 1];
279- // Handle both string content and array content
280- if (typeof lastMessage.content === 'string') {
281- reviewBody = lastMessage.content;
282- } else if (Array.isArray(lastMessage.content)) {
283- reviewBody = lastMessage.content
284- .filter(block => block.type === 'text')
285- .map(block => block.text)
286- .join('\n\n');
289+ const fileContent = fs.readFileSync(process.env.EXECUTION_FILE, 'utf8');
290+ const executionLog = JSON.parse(fileContent);
291+
292+ // Log structure for debugging
293+ console.log('Execution log type:', Array.isArray(executionLog) ? 'array' : typeof executionLog);
294+ if (!Array.isArray(executionLog)) {
295+ console.log('Execution log keys:', Object.keys(executionLog));
296+ }
297+
298+ // Helper function to extract text from assistant messages
299+ const extractFromMessages = (messages) => {
300+ const assistantMessages = messages.filter(m => m.role === 'assistant');
301+ if (assistantMessages.length > 0) {
302+ const lastMessage = assistantMessages[assistantMessages.length - 1];
303+ if (typeof lastMessage.content === 'string') {
304+ return lastMessage.content;
305+ } else if (Array.isArray(lastMessage.content)) {
306+ return lastMessage.content
307+ .filter(block => block.type === 'text')
308+ .map(block => block.text)
309+ .join('\n\n');
310+ }
287311 }
312+ return null;
313+ };
314+
315+ // Try different possible structures from claude-code-base-action
316+ if (Array.isArray(executionLog)) {
317+ // claude-code-base-action writes messages array directly to file
318+ const extracted = extractFromMessages(executionLog);
319+ if (extracted) {
320+ reviewBody = extracted;
321+ } else {
322+ console.log('No assistant messages found in array');
323+ reviewBody = '⚠️ No assistant response found in execution log.';
324+ }
325+ } else if (executionLog.result) {
326+ // Direct result field
327+ reviewBody = executionLog.result;
328+ } else if (executionLog.output) {
329+ // Output field
330+ reviewBody = executionLog.output;
331+ } else if (executionLog.response) {
332+ // Response field
333+ reviewBody = executionLog.response;
334+ } else if (executionLog.messages && Array.isArray(executionLog.messages)) {
335+ // Messages array inside object
336+ const extracted = extractFromMessages(executionLog.messages);
337+ if (extracted) {
338+ reviewBody = extracted;
339+ } else {
340+ reviewBody = '⚠️ No assistant response found in messages.';
341+ }
342+ } else if (typeof executionLog === 'string') {
343+ // Plain string
344+ reviewBody = executionLog;
345+ } else {
346+ // Fallback: stringify the whole thing
347+ console.log('Unknown format, raw content:', JSON.stringify(executionLog).substring(0, 500));
348+ reviewBody = '⚠️ Could not parse Claude response format. Check workflow logs for details.';
288349 }
289350 } catch (error) {
290351 console.error('Error parsing execution log:', error);
@@ -332,7 +393,23 @@ jobs:
332393 run : |
333394 echo "=== DRY RUN - Review would be posted as comment ==="
334395 echo ""
335- cat "$EXECUTION_FILE" | jq -r '.messages | map(select(.role == "assistant")) | last | .content' 2>/dev/null || cat "$EXECUTION_FILE"
396+ echo "=== Execution file structure ==="
397+ # Check if it's an array (claude-code-base-action format) or object
398+ if jq -e 'type == "array"' "$EXECUTION_FILE" >/dev/null 2>&1; then
399+ echo "Format: array of messages (length: $(jq 'length' "$EXECUTION_FILE"))"
400+ else
401+ jq 'keys' "$EXECUTION_FILE" 2>/dev/null || echo "Not valid JSON or file not found"
402+ fi
403+ echo ""
404+ echo "=== Attempting to extract review ==="
405+ # Try array format first (claude-code-base-action), then object formats
406+ if jq -e 'type == "array"' "$EXECUTION_FILE" >/dev/null 2>&1; then
407+ # Array format: extract last assistant message content
408+ jq -r '[.[] | select(.role == "assistant")] | last | if .content | type == "string" then .content elif .content | type == "array" then [.content[] | select(.type == "text") | .text] | join("\n\n") else "Could not extract" end' "$EXECUTION_FILE" 2>/dev/null || cat "$EXECUTION_FILE"
409+ else
410+ # Object format: try different possible paths
411+ jq -r '.result // .output // .response // .messages[-1].content // "Could not extract"' "$EXECUTION_FILE" 2>/dev/null || cat "$EXECUTION_FILE"
412+ fi
336413 echo ""
337414 echo "=== End of review ==="
338415
0 commit comments