Skip to content

Commit a8f8be4

Browse files
lapc506claude
andauthored
feat(gemini-review): parametrize model (--model) + adversarial mode (--adversarial) (#29)
liteLLM is provider-agnostic, but the one-shot worker hardcoded gemini/gemini-3.5-flash + GEMINI_API_KEY. Make the model a flag and route the required API-key env by provider prefix. Add --adversarial: a devil's-advocate rubric that challenges design / assumptions / trade-offs instead of approving. This is the cross-provider + adversarial premise of OpenAI's codex-plugin-cc, reusing the existing liteLLM architecture — no Codex CLI dependency, no new runtime. - scripts/gemini-code-review.sh: --model, --adversarial, provider->KEY_ENV map (gemini/*->GEMINI, openai|gpt*->OPENAI, anthropic|claude-*->ANTHROPIC); proxy api_key + startup/saved log lines use $MODEL. - commands/gemini-code-review.md: document the flags + when to use each. Tested: provider routing (openai model demands OPENAI_API_KEY even with GEMINI staged), --adversarial end-to-end on a live PR. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 47ff391 commit a8f8be4

2 files changed

Lines changed: 99 additions & 18 deletions

File tree

commands/gemini-code-review.md

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
description: Cheap first-pass code review on Gemini 3.5 Flash (one-shot via liteLLM), curated by the orchestrator against the local repo's CLAUDE.md. Design B — no nested agent.
3-
argument-hint: "[PR# | branch | --uncommitted] (default: <base>...HEAD)"
2+
description: Cheap first-pass code review (one-shot via liteLLM) on a parametrizable model — Gemini 3.5 Flash by default, or any provider via --model; supports --adversarial. Curated by the orchestrator against the local repo's CLAUDE.md. Design B — no nested agent.
3+
argument-hint: "[PR# | branch | --uncommitted] [--model <id>] [--adversarial] (default: <base>...HEAD, gemini-3.5-flash)"
44
---
55

66
# Gemini Code Review: $ARGUMENTS
@@ -21,20 +21,27 @@ Code agent runs on Gemini, so there is no tool-call-translation fragility.
2121
## Steps
2222

2323
1. **Run the worker** against the requested target (default `<base>...HEAD`, base
24-
auto-detected from `origin/HEAD``develop`/`main`/`master`). The worker needs
25-
`GEMINI_API_KEY` in the environment — provide it via this plugin's secret
26-
helpers so it never leaks into logs:
24+
auto-detected from `origin/HEAD``develop`/`main`/`master`). The worker is
25+
**multi-model**: `--model <litellm-id>` picks the backend (default
26+
`gemini/gemini-3.5-flash`; also `openai/gpt-5.4-mini`, `anthropic/claude-...`).
27+
It needs the **matching provider key** in the environment — `gemini/*`
28+
`GEMINI_API_KEY`, `openai|gpt*``OPENAI_API_KEY`, `anthropic|claude-*`
29+
`ANTHROPIC_API_KEY`. Provide it via the secret helpers so it never leaks:
2730

2831
```bash
2932
# one-time, if not already staged: /secret-input
33+
# default (Gemini):
3034
/secret-use GEMINI_API_KEY -- bash "${CLAUDE_PLUGIN_ROOT}/scripts/gemini-code-review.sh" $ARGUMENTS
35+
# cross-provider second opinion (kills sycophancy — different model, different blind spots):
36+
/secret-use OPENAI_API_KEY -- bash "${CLAUDE_PLUGIN_ROOT}/scripts/gemini-code-review.sh" --model openai/gpt-5.4-mini $ARGUMENTS
37+
# adversarial (devil's advocate — challenges design/assumptions/trade-offs):
38+
/secret-use GEMINI_API_KEY -- bash "${CLAUDE_PLUGIN_ROOT}/scripts/gemini-code-review.sh" --adversarial $ARGUMENTS
3139
```
3240

3341
- `$ARGUMENTS` may be a PR number, branch, `--uncommitted`, an `a..b` range, or
34-
empty. Pass it through verbatim.
35-
- If the script exits non-zero, surface the stderr reason (missing
36-
`GEMINI_API_KEY`, empty diff, proxy failed to start) and STOP — do not
37-
fabricate a review.
42+
empty — optionally with `--model <id>` and/or `--adversarial`. Pass it through verbatim.
43+
- If the script exits non-zero, surface the stderr reason (missing provider key,
44+
empty diff, proxy failed to start) and STOP — do not fabricate a review.
3845

3946
2. **Curate the Gemini output** — do NOT just relay it. For each finding:
4047
- **Validate** against the actual diff and **this repo's `CLAUDE.md`** (and any
@@ -53,5 +60,10 @@ Code agent runs on Gemini, so there is no tool-call-translation fragility.
5360

5461
- **`/make-no-mistakes:gemini-code-review`**: cheap triage, WIP self-review,
5562
large/low-risk diffs, fast feedback.
63+
- **`--model openai/gpt-5.4-mini` (or any other provider)**: a genuine
64+
cross-provider second opinion — a different model has different blind spots, so
65+
it catches what the default misses (same premise as OpenAI's `codex-plugin-cc`).
66+
- **`--adversarial`**: when you want the design challenged, not approved — surfaces
67+
unstated assumptions, rejected trade-offs, and failure modes on load-bearing PRs.
5668
- A full Claude-model review (e.g. a repo's own `/code-review`): authoritative
5769
review on high-risk PRs (auth, payments, migrations, RLS, infra).

scripts/gemini-code-review.sh

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@
1616
# gemini-code-review.sh my-branch # git diff <base>...my-branch
1717
# gemini-code-review.sh a..b # git diff a..b
1818
# [--base <branch>] # override auto-detected base
19+
# [--model <litellm-id>] # default gemini/gemini-3.5-flash; e.g. openai/gpt-5.4-mini, anthropic/claude-...
20+
# [--adversarial] # devil's-advocate review: challenge design, assumptions, trade-offs
1921
#
20-
# Secret: requires GEMINI_API_KEY in the environment. Provide it WITHOUT leaking
21-
# it into logs via this plugin's own secret helpers:
22+
# Multi-model: liteLLM routes by the model prefix. The required API-key env is
23+
# picked from the model: gemini/* -> GEMINI_API_KEY, openai|gpt* -> OPENAI_API_KEY,
24+
# anthropic|claude-* -> ANTHROPIC_API_KEY.
25+
#
26+
# Secret: requires the provider's API key in the environment. Provide it WITHOUT
27+
# leaking it into logs via this plugin's own secret helpers:
2228
# /secret-input (stage the key once)
23-
# /secret-use GEMINI_API_KEY -- bash <this-script> [args]
29+
# /secret-use <KEY_ENV> -- bash <this-script> [args] # e.g. OPENAI_API_KEY
2430
#
2531
# Requires: litellm, jq, curl, git (+ gh for PR mode).
2632
set -euo pipefail
@@ -29,10 +35,13 @@ PORT="${GEMINI_REVIEW_PORT:-4100}"
2935
MODEL="gemini/gemini-3.5-flash"
3036
BASE=""
3137
TARGET=""
38+
ADVERSARIAL=false
3239

3340
while [ $# -gt 0 ]; do
3441
case "$1" in
3542
--base) [ -z "${2:-}" ] && { echo "ERROR: --base requiere un valor." >&2; exit 1; }; BASE="$2"; shift 2 ;;
43+
--model) [ -z "${2:-}" ] && { echo "ERROR: --model requiere un valor (litellm id, p.ej. openai/gpt-5.4-mini)." >&2; exit 1; }; MODEL="$2"; shift 2 ;;
44+
--adversarial) ADVERSARIAL=true; shift ;;
3645
--uncommitted) TARGET="--uncommitted"; shift ;;
3746
-h|--help) sed -n '2,26p' "$0"; exit 0 ;;
3847
*) TARGET="$1"; shift ;;
@@ -42,10 +51,18 @@ done
4251
for bin in litellm jq curl git; do
4352
command -v "$bin" >/dev/null 2>&1 || { echo "ERROR: falta '$bin' en PATH." >&2; exit 1; }
4453
done
45-
if [ -z "${GEMINI_API_KEY:-}" ]; then
46-
echo "ERROR: GEMINI_API_KEY no está en el entorno." >&2
54+
55+
# --- provider -> API-key env (multi-model; liteLLM routes by model prefix) ---
56+
case "$MODEL" in
57+
gemini/*|google/*) KEY_ENV="GEMINI_API_KEY" ;;
58+
openai/*|gpt-*|o[0-9]*|*codex*) KEY_ENV="OPENAI_API_KEY" ;;
59+
anthropic/*|claude-*) KEY_ENV="ANTHROPIC_API_KEY" ;;
60+
*) KEY_ENV="GEMINI_API_KEY" ;;
61+
esac
62+
if [ -z "${!KEY_ENV:-}" ]; then
63+
echo "ERROR: ${KEY_ENV} no está en el entorno (requerido para el modelo '${MODEL}')." >&2
4764
echo "Stage it once with /secret-input, then run via:" >&2
48-
echo " /secret-use GEMINI_API_KEY -- bash \"$0\" $*" >&2
65+
echo " /secret-use ${KEY_ENV} -- bash \"$0\" $*" >&2
4966
exit 1
5067
fi
5168

@@ -125,6 +142,58 @@ Output EXACTLY this markdown:
125142
126143
If a section is empty, write "None.". Be specific with file:line; no vague advice.
127144
RUBRIC_EOF
145+
146+
# --- adversarial rubric (--adversarial): challenge design, not approve ---
147+
if $ADVERSARIAL; then
148+
read -r -d '' RUBRIC <<'RUBRIC_EOF' || true
149+
You are an ADVERSARIAL reviewer (devil's advocate). Your job is to CHALLENGE the
150+
change, not to approve it. Assume the author is competent and the happy path
151+
works; spend ALL your attention attacking the design itself. You are given ONLY
152+
the diff — do not ask to run commands; challenge what is shown.
153+
154+
For every meaningful change, attack:
155+
- Design & assumptions: what unstated assumption does this rely on, and when is
156+
it false? What implicit contract does it create, and who can violate it?
157+
- Trade-offs: what did this choice cost? What simpler/safer/cheaper alternative
158+
was rejected or never considered? Premature abstraction or optimization?
159+
- Failure modes: how does it break under concurrency, partial failure, retries,
160+
malicious input, scale, or a flaky dependency? What is the blast radius?
161+
- Boundaries: is the responsibility in the right layer/module? Does it leak
162+
state, invert a dependency, or couple things that should stay independent?
163+
- Steelman the rejection: what would a senior reviewer who wants to reject this
164+
PR say, and is that objection right?
165+
166+
Tie every challenge to a `file:line` and a concrete scenario. Do NOT list style
167+
nits or restate what the code does. Drop weak challenges; a mere "could be
168+
better" is Minor at most.
169+
170+
Output EXACTLY this markdown:
171+
172+
## Gemini Adversarial Review — {LABEL}
173+
174+
### Files Reviewed
175+
| File | +/- | Risk | Notes |
176+
|------|-----|------|-------|
177+
178+
### Challenges
179+
#### Critical (design is wrong / will break)
180+
- **[Category]** `file:line` — challenge -> what to reconsider
181+
#### Major (defend this before merge)
182+
- ...
183+
#### Minor (worth a second thought)
184+
- ...
185+
186+
### Assumptions This PR Bets On
187+
- ...
188+
189+
### Verdict: Defensible | Needs Defense | Reconsider Design
190+
| Critical | Major | Minor |
191+
|----------|-------|-------|
192+
| N | N | N |
193+
194+
If a section is empty, write "None.". Be specific; every point tied to file:line.
195+
RUBRIC_EOF
196+
fi
128197
RUBRIC="${RUBRIC/\{LABEL\}/$LABEL}"
129198

130199
# --- transient liteLLM proxy (self-generated config + master key; kill only ours) ---
@@ -136,7 +205,7 @@ model_list:
136205
- model_name: $MODEL
137206
litellm_params:
138207
model: $MODEL
139-
api_key: os.environ/GEMINI_API_KEY
208+
api_key: os.environ/$KEY_ENV
140209
general_settings:
141210
master_key: os.environ/LITELLM_MASTER_KEY
142211
litellm_settings:
@@ -148,7 +217,7 @@ cleanup() { $STARTED && [ -n "${PID:-}" ] && kill "$PID" 2>/dev/null || true; rm
148217
trap cleanup EXIT INT TERM
149218

150219
if ! ready; then
151-
echo "Levantando liteLLM proxy (gemini-3.5-flash) en 127.0.0.1:${PORT}..." >&2
220+
echo "Levantando liteLLM proxy (${MODEL}) en 127.0.0.1:${PORT}..." >&2
152221
litellm --config "$PROXY_CFG" --host 127.0.0.1 --port "$PORT" >"$PROXY_LOG" 2>&1 &
153222
PID=$!; STARTED=true
154223
for _ in $(seq 1 40); do ready && break; sleep 1; done
@@ -175,4 +244,4 @@ fi
175244

176245
OUT="$(mktemp -t gemini-code-review.XXXXXX.md)"
177246
printf '%s\n' "$CONTENT" | tee "$OUT"
178-
echo "[saved: $OUT · model: gemini-3.5-flash · diff: $LABEL · ${DIFF_CHARS} chars]" >&2
247+
echo "[saved: $OUT · model: ${MODEL} · diff: $LABEL · ${DIFF_CHARS} chars]" >&2

0 commit comments

Comments
 (0)