|
| 1 | +--- |
| 2 | +description: Run AI code review locally using OpenAI API before opening a PR |
| 3 | +argument-hint: "[--full-registry] [--model <model>] [--dry-run]" |
| 4 | +--- |
| 5 | + |
| 6 | +# Local AI Code Review |
| 7 | + |
| 8 | +Run a structured code review using the OpenAI Chat Completions API. Reviews changes |
| 9 | +against the same methodology criteria used by the CI reviewer, but adapted for local |
| 10 | +pre-PR use. Designed for iterative review/revision cycles before submitting a PR. |
| 11 | + |
| 12 | +## Arguments |
| 13 | + |
| 14 | +`$ARGUMENTS` may contain optional flags: |
| 15 | +- `--full-registry`: Include the entire REGISTRY.md instead of selective sections |
| 16 | +- `--model <name>`: Override the OpenAI model (default: `gpt-5.4`) |
| 17 | +- `--dry-run`: Print the compiled prompt without calling the API |
| 18 | + |
| 19 | +## Constraints |
| 20 | + |
| 21 | +This skill does not modify source code files. It may: |
| 22 | +- Create a commit if there are uncommitted changes (Step 3) |
| 23 | +- Create/update review artifacts in `.claude/reviews/` (gitignored) |
| 24 | +- Write temporary files to `/tmp/` (cleaned up in Step 8) |
| 25 | + |
| 26 | +Step 5 makes a single external API call to OpenAI. Step 3b runs a secret scan |
| 27 | +before any data is sent externally. |
| 28 | + |
| 29 | +## Instructions |
| 30 | + |
| 31 | +### Step 1: Parse Arguments |
| 32 | + |
| 33 | +Parse `$ARGUMENTS` for the optional flags listed above. All flags are optional — |
| 34 | +the default behavior (selective registry, gpt-5.4, live API call) requires no arguments. |
| 35 | + |
| 36 | +### Step 2: Validate Prerequisites |
| 37 | + |
| 38 | +Run these checks in parallel: |
| 39 | + |
| 40 | +```bash |
| 41 | +# Check API key is set (never echo/log the actual value) |
| 42 | +test -n "$OPENAI_API_KEY" && echo "API key: set" || echo "API key: MISSING" |
| 43 | + |
| 44 | +# Check script exists |
| 45 | +test -f .claude/scripts/openai_review.py && echo "Script: found" || echo "Script: MISSING" |
| 46 | +``` |
| 47 | + |
| 48 | +If the API key is missing (and not `--dry-run`): |
| 49 | +``` |
| 50 | +Error: OPENAI_API_KEY is not set. |
| 51 | +
|
| 52 | +To set it up: |
| 53 | +1. Get a key from https://platform.openai.com/api-keys |
| 54 | +2. Add to your shell: echo 'export OPENAI_API_KEY=sk-...' >> ~/.zshrc |
| 55 | +3. Reload: source ~/.zshrc |
| 56 | +``` |
| 57 | + |
| 58 | +If the script is missing: |
| 59 | +``` |
| 60 | +Error: .claude/scripts/openai_review.py not found. |
| 61 | +This file should be checked into the repository. |
| 62 | +``` |
| 63 | + |
| 64 | +Ensure the reviews directory exists: |
| 65 | +```bash |
| 66 | +mkdir -p .claude/reviews |
| 67 | +``` |
| 68 | + |
| 69 | +### Step 3: Commit Changes and Generate Diff |
| 70 | + |
| 71 | +Determine and validate the comparison ref (matching the pattern from `/push-pr-update`): |
| 72 | + |
| 73 | +```bash |
| 74 | +# Get the repo's default branch name |
| 75 | +default_branch=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null || echo "main") |
| 76 | + |
| 77 | +# Resolve to a validated local ref (fallback chain for shallow/single-branch clones) |
| 78 | +if git rev-parse --verify "$default_branch" >/dev/null 2>&1; then |
| 79 | + comparison_ref="$default_branch" |
| 80 | +elif git rev-parse --verify "origin/$default_branch" >/dev/null 2>&1; then |
| 81 | + comparison_ref="origin/$default_branch" |
| 82 | +else |
| 83 | + git fetch origin "$default_branch" --depth=1 2>/dev/null || true |
| 84 | + comparison_ref="origin/$default_branch" |
| 85 | +fi |
| 86 | +``` |
| 87 | + |
| 88 | +If the comparison ref still doesn't resolve, abort: |
| 89 | +``` |
| 90 | +Error: Cannot resolve comparison ref for '$default_branch'. Ensure you have |
| 91 | +fetched the default branch: git fetch origin $default_branch |
| 92 | +``` |
| 93 | + |
| 94 | +Check for uncommitted changes (modified, staged, or untracked): |
| 95 | +```bash |
| 96 | +git status --porcelain |
| 97 | +``` |
| 98 | + |
| 99 | +If there are uncommitted changes, commit them before proceeding: |
| 100 | + |
| 101 | +1. Show the user what will be committed (the `git status --porcelain` output above) |
| 102 | +2. Stage all changes: `git add -A` |
| 103 | +3. Create a commit with a descriptive message summarizing the changes. Follow the |
| 104 | + repository's commit message conventions (see recent `git log --oneline`). |
| 105 | +4. Report: "Committed changes: <commit message> (<short sha>)" |
| 106 | + |
| 107 | +If the commit fails (e.g., pre-commit hook), display the error and stop. |
| 108 | + |
| 109 | +Generate diff and metadata: |
| 110 | +```bash |
| 111 | +git diff --unified=5 "${comparison_ref}...HEAD" > /tmp/ai-review-diff.patch |
| 112 | +git diff --name-status "${comparison_ref}...HEAD" > /tmp/ai-review-files.txt |
| 113 | +branch_name=$(git branch --show-current) |
| 114 | +``` |
| 115 | + |
| 116 | +If the diff is empty, report: |
| 117 | +``` |
| 118 | +No committed changes vs ${comparison_ref} to review. |
| 119 | +``` |
| 120 | +Clean up temp files and stop. |
| 121 | + |
| 122 | +### Step 3b: Secret Scan Before API Upload |
| 123 | + |
| 124 | +Before sending any diff content to OpenAI, run the canonical secret scan patterns |
| 125 | +(from `/pre-merge-check` Section 2.6) against the same diff range: |
| 126 | + |
| 127 | +```bash |
| 128 | +# Content pattern — search diff content, output filenames only (never echo secret values) |
| 129 | +secret_files=$(git diff -G "(AKIA[A-Z0-9]{16}|ghp_[a-zA-Z0-9]{36}|sk-[a-zA-Z0-9]{48}|gho_[a-zA-Z0-9]{36}|[Aa][Pp][Ii][_-]?[Kk][Ee][Yy][[:space:]]*[=:]|[Ss][Ee][Cc][Rr][Ee][Tt][_-]?[Kk][Ee][Yy][[:space:]]*[=:]|[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][[:space:]]*[=:]|[Pp][Rr][Ii][Vv][Aa][Tt][Ee][_-]?[Kk][Ee][Yy]|[Bb][Ee][Aa][Rr][Ee][Rr][[:space:]]+[a-zA-Z0-9_-]+|[Tt][Oo][Kk][Ee][Nn][[:space:]]*[=:])" --name-only "${comparison_ref}...HEAD" 2>/dev/null || true) |
| 130 | + |
| 131 | +# Sensitive filename pattern |
| 132 | +sensitive_files=$(git diff --name-only "${comparison_ref}...HEAD" | grep -iE "(\.env|credentials|secret|\.pem|\.key|\.p12|\.pfx|id_rsa|id_ed25519)$" || true) |
| 133 | +``` |
| 134 | + |
| 135 | +If either `secret_files` or `sensitive_files` is non-empty, use AskUserQuestion: |
| 136 | +``` |
| 137 | +Warning: Potential secrets detected in files that would be sent to OpenAI: |
| 138 | +- <list filenames> |
| 139 | +
|
| 140 | +The diff containing these files is about to be transmitted to the OpenAI API. |
| 141 | +
|
| 142 | +Options: |
| 143 | +1. Abort — review and remove secrets before retrying |
| 144 | +2. Continue — I confirm these are not real secrets |
| 145 | +``` |
| 146 | + |
| 147 | +If user selects abort, clean up temp files and stop. If continue, proceed. |
| 148 | + |
| 149 | +### Step 4: Handle Re-Review State |
| 150 | + |
| 151 | +If `.claude/reviews/local-review-latest.md` exists, preserve it for re-review context: |
| 152 | +```bash |
| 153 | +if [ -f .claude/reviews/local-review-latest.md ]; then |
| 154 | + cp .claude/reviews/local-review-latest.md .claude/reviews/local-review-previous.md |
| 155 | + echo "Previous review preserved for re-review context." |
| 156 | +fi |
| 157 | +``` |
| 158 | + |
| 159 | +### Step 5: Run the Review Script |
| 160 | + |
| 161 | +Build and run the command. Include `--previous-review` only if the previous review file |
| 162 | +exists from Step 4. |
| 163 | + |
| 164 | +```bash |
| 165 | +python3 .claude/scripts/openai_review.py \ |
| 166 | + --review-criteria .github/codex/prompts/pr_review.md \ |
| 167 | + --registry docs/methodology/REGISTRY.md \ |
| 168 | + --diff /tmp/ai-review-diff.patch \ |
| 169 | + --changed-files /tmp/ai-review-files.txt \ |
| 170 | + --output .claude/reviews/local-review-latest.md \ |
| 171 | + --branch-info "$branch_name" \ |
| 172 | + [--previous-review .claude/reviews/local-review-previous.md] \ |
| 173 | + [--full-registry] \ |
| 174 | + [--model <model>] \ |
| 175 | + [--dry-run] |
| 176 | +``` |
| 177 | + |
| 178 | +If `--dry-run`: display the prompt output and stop. Report the estimated token count |
| 179 | +and model that would be used. |
| 180 | + |
| 181 | +If the script exits non-zero, display the error output and stop. |
| 182 | + |
| 183 | +### Step 6: Display the Review |
| 184 | + |
| 185 | +Read and display the full contents of `.claude/reviews/local-review-latest.md`. |
| 186 | + |
| 187 | +### Step 7: Summarize Findings and Offer Next Steps |
| 188 | + |
| 189 | +Parse the review output to extract ALL findings. For each finding, capture: |
| 190 | +- Severity (P0/P1/P2/P3) |
| 191 | +- Section (Methodology, Code Quality, etc.) |
| 192 | +- One-line summary of the issue |
| 193 | + |
| 194 | +Present a **findings summary** showing every finding, grouped by severity: |
| 195 | + |
| 196 | +``` |
| 197 | +## Review Summary: <assessment emoji and label> |
| 198 | +
|
| 199 | +### P0 — Blockers |
| 200 | +1. [Methodology] <one-line summary> — <file:line> |
| 201 | +2. [Security] <one-line summary> — <file:line> |
| 202 | +
|
| 203 | +### P1 — Needs Changes |
| 204 | +1. [Code Quality] <one-line summary> — <file:line> |
| 205 | +
|
| 206 | +### P2 — Should Fix |
| 207 | +1. [Documentation] <one-line summary> — <file:line> |
| 208 | +
|
| 209 | +### P3 — Informational (no action required) |
| 210 | +1. [Maintainability] <one-line summary> — <file:line> |
| 211 | +2. [Tech Debt] <one-line summary> — <file:line> |
| 212 | +``` |
| 213 | + |
| 214 | +Omit severity groups that have zero findings. The full review with details is already |
| 215 | +displayed in Step 6 — this summary helps the user quickly assess what needs attention. |
| 216 | + |
| 217 | +Then use AskUserQuestion, tailored to the severity: |
| 218 | + |
| 219 | +**If no findings at all** (clean review): |
| 220 | +``` |
| 221 | +Review passed with no findings. Suggested next steps: |
| 222 | +- /submit-pr — commit and open a pull request |
| 223 | +``` |
| 224 | + |
| 225 | +**For ⛔ or ⚠️ (P0/P1 findings)**: |
| 226 | +``` |
| 227 | +Options: |
| 228 | +1. Enter plan mode to address findings (Recommended) |
| 229 | +2. Re-run with --full-registry for deeper methodology context |
| 230 | +3. Skip — I'll address these manually |
| 231 | +``` |
| 232 | + |
| 233 | +**For ✅ with P2/P3 findings only**: |
| 234 | +``` |
| 235 | +Options: |
| 236 | +1. Address findings before submitting |
| 237 | +2. Skip — proceed to /submit-pr |
| 238 | +``` |
| 239 | + |
| 240 | +**If user chooses to address findings**: Parse the findings from the review output. |
| 241 | +The review context is already in the conversation. Start addressing the findings |
| 242 | +directly — for P0/P1 issues use `EnterPlanMode` for a structured approach; for P2/P3 |
| 243 | +issues, fix them directly since they are minor. |
| 244 | + |
| 245 | +After fixes are committed, the user re-runs `/ai-review-local` for a follow-up review. |
| 246 | + |
| 247 | +### Step 8: Cleanup |
| 248 | + |
| 249 | +```bash |
| 250 | +rm -f /tmp/ai-review-diff.patch /tmp/ai-review-files.txt |
| 251 | +``` |
| 252 | + |
| 253 | +## Error Handling |
| 254 | + |
| 255 | +| Scenario | Response | |
| 256 | +|---|---| |
| 257 | +| `OPENAI_API_KEY` not set (non-dry-run) | Error with setup instructions (see Step 2) | |
| 258 | +| Script file missing | Error suggesting it should be checked in | |
| 259 | +| No committed changes | Clean exit with message | |
| 260 | +| Script exits non-zero | Display stderr output from script | |
| 261 | +| Previous review file missing on re-run | Script warns and continues as fresh review | |
| 262 | +| User aborts due to uncommitted changes | Clean exit | |
| 263 | + |
| 264 | +## Examples |
| 265 | + |
| 266 | +```bash |
| 267 | +# Standard review of current branch vs main |
| 268 | +/ai-review-local |
| 269 | + |
| 270 | +# Review with full methodology registry |
| 271 | +/ai-review-local --full-registry |
| 272 | + |
| 273 | +# Preview the compiled prompt without calling the API |
| 274 | +/ai-review-local --dry-run |
| 275 | + |
| 276 | +# Use a different model |
| 277 | +/ai-review-local --model gpt-4.1 |
| 278 | +``` |
| 279 | + |
| 280 | +## Notes |
| 281 | + |
| 282 | +- This skill does NOT modify source files — it only generates temp files and |
| 283 | + review artifacts in `.claude/reviews/` (which is gitignored). It may also |
| 284 | + create a commit if there are uncommitted changes (Step 3). |
| 285 | +- Re-review mode activates automatically when a previous review exists in |
| 286 | + `.claude/reviews/local-review-latest.md` |
| 287 | +- The review criteria are adapted from `.github/codex/prompts/pr_review.md` (same |
| 288 | + methodology axes, severity levels, and anti-patterns) but framed for local |
| 289 | + code-change review rather than PR review |
| 290 | +- The CI review (Codex action with full repo access) remains the authoritative final |
| 291 | + check — local review is a fast first pass to catch most issues early |
| 292 | +- **Data transmission**: In non-dry-run mode, this skill transmits the unified diff, |
| 293 | + changed-file metadata, selected methodology registry text, and prior review context |
| 294 | + (if present) to OpenAI via the Chat Completions API. Use `--dry-run` to preview |
| 295 | + exactly what would be sent. |
| 296 | +- This skill pairs naturally with the iterative workflow: |
| 297 | + `/ai-review-local` -> address findings -> `/ai-review-local` -> `/submit-pr` |
0 commit comments