From 63dec7551d05ab79ddebf0ca6ec46f702b597862 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:38:15 +0000 Subject: [PATCH] feat(agent): add automated-pr-review script (agent) Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- scripts/agent/automated-pr-review.mjs | 127 ++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 scripts/agent/automated-pr-review.mjs diff --git a/scripts/agent/automated-pr-review.mjs b/scripts/agent/automated-pr-review.mjs new file mode 100644 index 00000000..19ed4dfd --- /dev/null +++ b/scripts/agent/automated-pr-review.mjs @@ -0,0 +1,127 @@ +import { spawnSync, execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; + +const REPORT_FILE = 'PR_REVIEW.md'; +const APPLY_FIXES = process.argv.includes('--apply-fixes'); + +function run(command, args, opts = {}) { + console.log(`> ${command} ${args.join(' ')}`); + return spawnSync(command, args, { encoding: 'utf-8', stdio: 'pipe', ...opts }); +} + +function getDiffFiles() { + try { + // Try to compare against origin/main, fallback to HEAD~1 or just check current changes if no upstream + // Since this runs on a checkout, we want to see what this branch touches compared to main. + // If origin/main is not available, we might fail. + const hasMain = run('git', ['rev-parse', '--verify', 'origin/main']).status === 0; + const base = hasMain ? 'origin/main' : 'HEAD'; + + // Check if we are on main? No, we are on a PR branch usually. + // We want the diff of the *current* code vs main. + const res = run('git', ['diff', '--name-only', base, 'HEAD']); + if (res.error || res.status !== 0) return []; + return res.stdout.trim().split('\n').filter(Boolean); + } catch (e) { + console.error("Error getting diff:", e); + return []; + } +} + +function main() { + const report = []; + report.push('# Automated PR Review'); + report.push(`Date: ${new Date().toISOString()}`); + report.push(''); + + // 1. npm ci + console.log('Running npm ci...'); + const ci = run('npm', ['ci']); + if (ci.status !== 0) { + report.push('## 🚨 Dependency Install Failed'); + report.push('```'); + report.push(ci.stderr || ci.stdout); + report.push('```'); + // If ci fails, we probably can't run other scripts, but we try. + } else { + report.push('## ✅ Dependencies Installed'); + } + + // 2. Format + console.log('Running npm run format...'); + run('npm', ['run', 'format']); // This modifies files + + const status = run('git', ['status', '--porcelain']).stdout; + const formattedFiles = status.split('\n').filter(l => l.trim()).map(l => l.slice(3)); + + if (formattedFiles.length > 0) { + report.push('## ⚠️ Formatting Changes'); + report.push('The following files were reformatted:'); + report.push(formattedFiles.map(f => `- ${f}`).join('\n')); + + if (APPLY_FIXES) { + console.log('Applying formatting fixes...'); + run('git', ['add', '.']); + run('git', ['commit', '-m', 'fix(ai): automated formatting (agent)']); + report.push('\n**Changes have been automatically committed.**'); + } else { + report.push('\n**Run `npm run format` to fix these issues.**'); + } + } else { + report.push('## ✅ Formatting Passed'); + } + + // 3. Lint + console.log('Running npm run lint...'); + const lint = run('npm', ['run', 'lint']); + if (lint.status !== 0) { + report.push('## ⚠️ Lint Warnings/Errors'); + report.push('
Show Lint Output'); + report.push(''); + report.push('```'); + report.push(lint.stdout + '\n' + lint.stderr); + report.push('```'); + report.push('
'); + } else { + report.push('## ✅ Lint Passed'); + } + + // 4. Unit Tests + console.log('Running npm run test:unit...'); + const test = run('npm', ['run', 'test:unit']); + if (test.status !== 0) { + report.push('## ❌ Unit Tests Failed'); + report.push('
Show Test Output'); + report.push(''); + report.push('```'); + report.push(test.stdout + '\n' + test.stderr); + report.push('```'); + report.push('
'); + } else { + report.push('## ✅ Unit Tests Passed'); + } + + // 5. Guardrails + const diffFiles = getDiffFiles(); + const sensitivePatterns = [ + /^js\/nostr\//, + /^js\/services\/storageService/, + /^js\/services\/r2Service/, + /crypto/i + ]; + + const sensitiveChanges = diffFiles.filter(f => sensitivePatterns.some(p => p.test(f))); + + if (sensitiveChanges.length > 0) { + report.push('## 🛡️ Security & Protocol Review Required'); + report.push('The following sensitive files were modified:'); + report.push(sensitiveChanges.map(f => `- ${f}`).join('\n')); + report.push('\n**Flags:** `requires-security-review`, `requires-protocol-review`'); + } + + fs.writeFileSync(REPORT_FILE, report.join('\n')); + console.log(`Report generated at ${REPORT_FILE}`); +} + +main();