@empanel/cli
v0.10.0
Published
Empanel — a multi-model code-review gate: empanel a jury of diverse models; a thin deterministic spine owns the verdict + trust boundary.
Maintainers
Readme
Empanel
A multi-model review gate: empanel a jury of diverse models over a PR — or a whole repo — and land on a verdict you can trust.
Naming note: this project was previously distributed as the
review-gateplugin in thesmarzban-skillsmarketplace. Same spine, same trust boundary — now a standalone product named Empanel, renamed throughout (CLI, skills, env vars). The legacy name survives only in that marketplace's last plugin release, which the marketplace serves until the first Empanel release replaces it.
Multi-model AI code-review gate + whole-repo audit, packaged as a Claude Code or Cursor plugin. An agent orchestrates holistic code reviews across several diverse models; a thin deterministic spine owns the block/pass verdict, the no-silent-dismissal rule, and the single PR comment — so the reviewers can be untrusted: a prompt-injected diff or a steered reviewer cannot flip the gate, and every dismissal is forced into the open — written, attributed, auditable (tool-detected facts can't be dismissed at all).
- Multi-model panel — holistic review across up to four diverse
provider:modellineages (e.g.ollama:kimi-k2.7-code,ollama:glm-5.2,anthropic:claude-opus-4-8,openai-codex:gpt-5.5) run through thepirunner, plus conditional lenses fired by trigger (security, tests, subtle-correctness, simplify, …). - Untrusted reviewers, trustworthy verdict — the verdict is computed by code from structured findings, not by any model.
- No silent dismissal — a model gating finding can be dismissed only with a written justification; a tool-detected fact (committed secret, conflict marker) can't be dismissed at all.
- Deterministic scan tier —
$0git-hygiene + optionalgitleaks/osv-scanner, all fail closed. - Two skills, one spine —
gate(per-PR merge gate) andrepo-audit(advisory whole-repo health audit).
Distilled from a bake-off against a 30-reviewer specialist panel: a clean holistic prompt across a few diverse models matched or beat the specialists at far lower cost, and the durable value was the deterministic spine — not the specialization. This keeps the spine, drops the fan-out.
📚 Full documentation: docs/.
How it works
PR ─► agent checks out the branch (worktree)
─► reviewers (UNTRUSTED, read-only): each is a diverse model in an agent harness,
told "review this PR" — it explores the repo itself (git diff, read, grep) → findings JSON
holistic × N diverse models + conditional lenses fired by trigger
─► tools (TRUSTED, deterministic): `scan` runs git-diff scanners (conflict markers, focused
tests, committed secrets/artifacts) → findings JSON, merged into the SAME pool
─► consolidate (cluster by location, agreement across models)
─► agent adjudicates contested clusters ─► spine enforces no-silent-dismissal
─► decide ─► verdict (block/pass) + the gate findings comment (verdict + reviewer roster)
─► agent posts a separate orchestrator review comment (what's implemented / not · approve|changes)
─► CI required-check uses the verdict to block/allow mergeThe agent does the flexible reviewing; the spine (consolidate + decide, pure code) owns the
verdict and the trust boundary. Why this is safe even with untrusted reviewers:
docs/technical/trust-boundary.md.
Quickstart
Install the CLI from npm, then run the first-run wizard:
npm install -g @empanel/cli@0 # the CLI spine, major-pinned (or one-off: npx @empanel/cli@0 <verb>)
empanel setup # connect a provider + write the roster config (interactive, $0 by default)Then install the two orchestration skills as a plugin — this repo is its own marketplace, serving the
same SKILL.md files to both harnesses:
# Claude Code:
/plugin marketplace add smarzban/empanel
/plugin install empanel
# Cursor (2.5+): Settings → Plugins → Import → paste https://github.com/smarzban/empanelThe plugin is thin — the skills drive @empanel/cli via npx @empanel/cli@0 (major-pinned), not a
bundled CLI. Now ask your agent: "Review this PR and decide whether it can merge: #123." → triggers
the gate skill (skills/gate/SKILL.md).
You also need git, gh, and at least one connected model provider (empanel setup connects one;
or by hand, empanel auth login <provider> — e.g. anthropic, openai-codex, or a custom ollama
provider). Full walk-through: docs/quickstart.md.
Install
@empanel/cli ships the committed compiled spine, so there's no build on install and the spine has
no runtime dependencies for scan/collect/consolidate/decide/check. Running a review (the
pi backend) uses the one pinned runtime dependency @earendil-works/pi-ai (installed with the
package); git is required for the diff/scan. From-source install, prerequisites, provider connections,
and optional scanners (gitleaks, osv-scanner): docs/install/install.md.
CLI
The empanel CLI is on your PATH once installed (or via npx @empanel/cli@0; the skills call it for you):
empanel prompt <name> # print a reviewer/audit prompt + its output contract
empanel run <reviewerId> pi <provider:model> <repoDir> <prompt> # one reviewer (untrusted)
empanel scan <repoDir> <baseRef> # deterministic tier (trusted, no LLM)
empanel consolidate <outputs.json> # cluster + agreement
empanel decide <clusters.json> <adjudications.json> <meta.json> # deterministic verdict + PR commentReference: docs/usage/cli.md. The prompt catalog (holistic, the lenses, the audit passes, the contracts): docs/usage/prompts.md.
Configuration
Most tunables are EMPANEL_* environment variables — all with safe defaults; the reviewer's
per-round budgets live in the global config (~/.empanel/config.json, written by empanel setup;
--config/EMPANEL_CONFIG pin an override — a repo checkout is never a config source). The full
reference is in docs/install/configuration.md.
Trust note. The scanners defend the repo they scan. When gating untrusted PRs, pin a trusted config outside the checkout (
EMPANEL_GITLEAKS_CONFIG/EMPANEL_OSV_CONFIG) so a committed.gitleaks.tomlcan't weaken scanning; a change to an in-repo scanner policy file is itself flagged, and a configured-but-failing scanner fails closed (blocks), never silently passes.
Releasing & updates
Two version surfaces: the npm CLI (@empanel/cli, released by a v<version> git tag via
release.yml — OIDC trusted publishing, no stored token, automatic provenance) and the Claude Code +
Cursor plugin (.claude-plugin/plugin.json + .cursor-plugin/plugin.json pinned semver, kept in
lockstep; installed copies update only when the version string changes). As of 0.10.0 both surfaces
carry the same version and move together.
The CLI ships 0.x during the private shakedown; 1.0.0 is the public-flip version.
Details: docs/technical/plugin-and-releases.md.
Documentation
Full docs live in docs/, routed by audience:
- Use it → docs/quickstart.md, docs/usage/ (the gate, the audit, the CLI, the prompts).
- Install & configure → docs/install/install.md, docs/install/configuration.md.
- Understand or extend → docs/technical/architecture.md, trust-boundary.md, consolidate-and-decide.md, extending.md.
Layout
.claude-plugin/ marketplace.json + plugin.json (Claude Code plugin manifest)
.cursor-plugin/ marketplace.json + plugin.json (Cursor plugin manifest — same skills/)
skills/ gate/ (the PR merge gate) · repo-audit/ (the whole-repo audit)
src/ the spine: runner · scan · consolidate · decide · proc · prompts · cli · types
dist/ the committed compiled spine the launcher runs
bin/empanel the PATH launcher (resolves dist/cli.js relative to itself)
prompts/ holistic + 10 lenses + verify + dismissal-audit + 8 audit passes + 3 output contracts + backends
tests/ npm test, no networkDevelopment
npm install # dev deps (tsx, typescript, vitest)
npm test # full suite, no network
npm run typecheck # tsc --noEmit (src + tests)
npm run build # tsc → dist/
npm run build:check # build, then fail if committed dist/ drifts from src/Adding a lens or audit pass, and the test discipline:
docs/technical/extending.md. Rebuild and commit dist/ whenever you
change src/ (prompt/skill markdown changes don't touch dist/).
