Draft cheap. Review expensive.
Side-Eye is an independent quality-control layer for AI coding agents. A cheap model does the work; a stronger judge reviews the session, the diff, and the evidence — then returns a structured verdict without letting the drafting model grade itself.
- Native commands in Claude Code and OpenCode
- A Codex CLI adapter today
- A provider-neutral Rust core, with Python only as a behavioral oracle
- Estimate first, confirm before spend
- Self-grading, secret, context, and budget guards
Demo: https://redhat-et.github.io/sideeye/
Latest release: https://github.com/redhat-et/sideeye/releases/tag/v0.6.22
uv tool install git+https://github.com/redhat-et/sideeye
# or: pipx install git+https://github.com/redhat-et/sideeye
# or: pip install git+https://github.com/redhat-et/sideeyeThen run the one-time setup:
sideeye setup # guided wizard
sideeye setup --repo /path/to/repo # or point at it explicitly
sideeye setup --model claude-opus-4-8 --max-tokens 4096
sideeye setup --yes # accept defaults (scripts)Setup is a guided flow:
- shows the detected harnesses;
- asks only for missing route/model/repo values;
- prompts once for a hidden judge credential;
- stores it in Side-Eye config with mode
600; - installs and configures the bundled OpenCode plugin;
- preserves unrelated JSON/JSONC settings;
- selects the Rust engine when
sideeyeis onPATH; - verifies the route with a free
count_tokenscall when interactive.
Run sideeye setup --help for examples and automation flags.
Migration note: setup and every harness path use the Rust
sideeyebinary. The Python CLI remains only as a behavioral oracle for conformance and research tools. Seedocs/rust-migration-plan.md.
Check the judge route without spending:
sideeye doctor # offline: route guard must say "passed"
sideeye doctor --verify # free route check (count_tokens or /v1/models)The judge must be a real, stronger route — never the cheap model that drafted the session. Providers share one packet/verdict contract:
- Anthropic-compatible (
--provider anthropic): exact freecount_tokensestimate and Claude pricing. - OpenAI-compatible / local (
--provider openai-compatible): OpenAI, gateways, Ollama, LM Studio, and vLLM. The estimate is labeled approximate because OpenAI has no free count endpoint; cost is reported as unknown unless the route provides its own metering. Tool calling is required.
Output caps are provider-aware: 8192 for Anthropic and 16384 for
OpenAI-compatible routes. GLM and DeepSeek reasoning tokens count toward the
output cap, so 4096 is too small for a real GLM review. Use
sideeye setup --max-tokens N to change it. The estimate prices the full cap,
so it is conservative.
Context windows are model-aware: 1,000,000 for GLM and DeepSeek,
200,000 for Claude. Packets that exceed the window are auto-trimmed using
the free token count. Slow routes can be given a longer client timeout with
SIDEEYE_JUDGE_TIMEOUT_SECS=600.
Local example:
sideeye setup \
--provider openai-compatible \
--judge-base-url http://127.0.0.1:11434 \
--judge-api-key local \
--model qwen2.5-coderResolution order:
--base-urlSIDEEYE_JUDGE_BASE_URL+SIDEEYE_JUDGE_API_KEY~/.config/sideeye/config.json(or$SIDEEYE_CONFIG)- ambient
ANTHROPIC_API_KEY, orANTHROPIC_BASE_URL+ANTHROPIC_AUTH_TOKEN
A key alone means “Anthropic direct”. A gateway route needs an explicit base URL.
Ambient Anthropic credentials are not sent to a foreign base unless you set
judge.trust_ambient_route.
The plugin adds one native TUI command. With no arguments it first asks for optional focus instructions (type and press Enter), then opens the scope selector (arrow keys + Enter):
/sideeye # pick Last exchange or Whole session
/sideeye last # estimate the last request + response
/sideeye all # estimate the whole session
/sideeye last --confirm # submit the last exchange
/sideeye all --confirm # submit the whole session
/sideeye all security and missing tests
The scope selector shows the free estimate and asks for confirmation before any
judge spend. If the packet is too large for the judge context, Side-Eye shrinks
the packet automatically using the free token count and retries; the estimate
says when it was auto-trimmed. Extra text is reviewer instruction on top of the
rubric; only --confirm is special. The old sideeye-review server command and alias were
removed — /sideeye is the single user-facing command.
Flow:
OpenCode export -> ReviewPacket -> redact -> budget -> estimate -> judge -> verdict
The verdict opens in a native Side-Eye dialog: score, severity, summary,
issues with evidence, judge/cost, packet/rubric, and the saved raw-record path.
You then label it Confirmed, False positive, Neutral, or skip. The
label is stored locally for metrics; the verdict is not inserted into the
model’s context, so the assistant does not automatically respond to it.
After you label it, a short Side-Eye review complete summary is posted to the
session (score, issue count, label, ledger path), so both you and the assistant
see that the review was received. The full verdict stays in the dialog and the
raw ledger; /sideeye show reopens it and /sideeye label confirmed labels it
later. Preparation and judging use a modal dialog, not a toast.
sideeye setup installs a personal /sideeye skill. The plugin marketplace
also provides /sideeye:ask for users who prefer plugin distribution.
/sideeye --validate # free route check
/sideeye is my last change sound? # scoped second opinion
/sideeye --all # full-session review
sideeye setup installs the sideeye skill under ~/.agents/skills/sideeye.
Invoke it from Codex with /skills sideeye (or $sideeye) and use the same
modes:
/skills sideeye --validate
/skills sideeye is this change sound?
/skills sideeye --all
sideeye setup # configure credentials and harnesses (Rust)
sideeye doctor # resolved route guard, no spend
sideeye session --client claude --current
sideeye session --client codex --current
sideeye advise --client claude --current --question "..."
sideeye label confirmed # label the newest stored verdict
sideeye stats # local precision/coverage/costSession reviews persist to ~/.local/share/sideeye/<client>-verdicts.jsonl.
Use sideeye stats --ledger ~/.local/share/sideeye/claude-verdicts.jsonl for a
non-OpenCode ledger, or pass the same --ledger to label.
Labels are local-only and privacy-preserving. The label ledger stores only the record id, label, and timestamp:
sideeye label confirmed # newest verdict
sideeye label false-positive --id <record_id>
sideeye label neutral --id <record_id>
sideeye stats
sideeye stats --jsonstats reports coverage, confirmed/false-positive/neutral counts, precision,
false-positive rate, total judge cost, and cost per confirmed finding. Catch
rate is intentionally unavailable until there is a labeled defect benchmark.
No prompts, code, paths, or keys are uploaded.
The Rust workspace is the migration target:
| Crate | Role |
|---|---|
sideeye-core |
ReviewPacket, Verdict, validation, redaction, budgeting |
sideeye-capture |
OpenCode, Claude Code, and Codex adapters; git diff/manifest |
sideeye-providers |
Anthropic, OpenAI-compatible/local, deterministic fake, advice |
sideeye-cli |
setup, doctor, session, advise, validate, packet, estimate, review |
sideeye doctor
sideeye validate packet.json
sideeye packet --opencode-export export.json --redact \
--repo . --touched-file src/parser.rs
sideeye estimate packet.json --model claude-opus-4-8
sideeye review packet.json --provider anthropic --model claude-opus-4-8Python remains only as a temporary behavioral oracle for conformance and the
research sampler/calibration tools. No harness path invokes it. See
docs/rust-core.md.
- No self-grading. A judge that generated any turn in the packet is refused.
- Redaction before remote judging. Credential-shaped values are replaced.
- Context guard. Oversized packets fail before spend.
- Budgeting. Transcripts and diffs are trimmed by salience, and the packet says when its view is partial.
- Estimate then confirm. The first invocation is free and never calls the judge.
- Separate streams. Random benchmark samples, human escalations, and dogfood reviews are never pooled.
- Raw verdicts. Structured records are stored and displayed without model paraphrase.
- CI: Rust format/tests/clippy/release build, Python tests/version/demo checks, OpenCode plugin typecheck/build/tests/package.
- CD: tag
v*publishes Linux x86_64 and macOS arm64 binaries, SHA-256 checksums, Python wheel/sdist, and the OpenCode plugin tarball. - Pages:
docs/publishes the demo and docs.
See docs/releasing.md.
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
pytest -q
(cd integrations/opencode && npm ci && npm run typecheck && npm test)Current coverage: 169 Python tests, 73 Rust tests, 12 OpenCode plugin tests.
crates/ Rust core, capture, providers, CLI
adapters/ Python conformance-oracle adapters
judge/ Python oracle judge, schema, transcript
integrations/opencode/ native OpenCode TUI plugin
rubric/ grading rubrics
scripts/ version, demo, and release checks
docs/ demo, architecture, scope, releasing
configure.py one-command setup
escalate.py, advise.py Python oracle review/advice engines
Working today: Claude Code plugin, OpenCode native plugin, Codex CLI adapter, Rust contracts/capture/estimate/Anthropic judge, secure setup, releases, demo.
Next, in order:
- provider-neutral judging: OpenAI-compatible, gateway, and local providers (#3);
- Rust session review +
advise, then native Claude Code and Codex paths; - local judge-success ledger and
sideeye stats(#5); - cross-harness conformance CI (#2);
- raw verdict viewer and scoreboard;
- OpenClaw adapter (#7).
Windows support is explicitly out of scope for now (#32).