npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

adversarial-review

v2.10.0

Published

Adversarial, ship/no-ship code review of a git diff or branch — runs against any LLM (API or local CLI agent) and returns structured findings

Downloads

2,634

Readme

adversarial-review

npm version CI License

Skeptical, ship/no-ship code review of a git diff or branch — run against any LLM.

The reviewer's only job is to break confidence in a change, not validate it. It hunts for the strongest reasons a change should not ship yet, prioritizing the failure classes that are expensive, dangerous, or hard to detect: auth and trust boundaries, injection, secrets, data loss, rollback safety, race conditions, schema drift, supply-chain and CI/CD changes, test weakening, and observability gaps. Output is structured JSON — a verdict, a terse summary, coverage, grounded findings (severity, category, file, line range, confidence, exploit scenario, quoted evidence, recommendation), and next steps.

It collects your git context, builds the prompt, calls a model (Anthropic / OpenAI / Gemini API, or a local CLI agent like claude / codex / agy) using the provider's native structured-output mode, validates the response against the JSON Schema, grounds each finding against the actual change set, and prints a report. The exit code is derived deterministically from the findings (severity + confidence thresholds), so it drops straight into CI and pre-push hooks — and it fails closed: a git collection failure exits 1, never a silent approve.

Beyond a single pass it can fan the same review out across multiple independent provider families and gate on a quorum (--providers), run an autonomous review → fix → re-review convergence loop (--loop), and review artifacts — specs, tickets, rail-sets — instead of a diff (--input).

The review prompt (prompt-template.md) and output schema (schema.json) are derived from the OpenAI Codex adversarial-review skill (Copyright 2026 OpenAI). They have been generalized, extended, and stripped of the Codex-specific runtime so the tool works with any model. This project is licensed under the Apache License, Version 2.0; see NOTICE for attribution details.

Install

npm install -g adversarial-review
# or run ad hoc:
npx adversarial-review --help

Installing as a Claude Code skill

The npm package ships a bundled Claude Code skill under skills/adversarial-review/. Installing it lets Claude Code invoke the review automatically when you type phrases like "review this branch" or "is this safe to ship", and gives it the full three-tier fallback (Tier 3 works with no CLI and no API key).

Global skill (available in all projects):

# After npm install -g:
cp -r "$(npm root -g)/adversarial-review/skills/adversarial-review" ~/.claude/skills/

# Or from a cloned repo:
cp -r skills/adversarial-review ~/.claude/skills/

Project skill (this repo only):

mkdir -p .claude/skills
cp -r "$(npm root -g)/adversarial-review/skills/adversarial-review" .claude/skills/

Restart Claude Code after copying. Verify with /find-skills adversarial-review or by asking Claude Code "what skills are available?"

Usage

# Review uncommitted working-tree changes
npx adversarial-review

# Review the current branch against main
npx adversarial-review --base main

# Add a focus area (weighted heavily by the reviewer)
npx adversarial-review "focus on the token refresh path"

# Higher recall and precision: sample the reviewer twice, then try to refute
# each finding and drop the ones that don't survive
npx adversarial-review --passes 2 --verify

# Give an API model more code to reason about than the diff hunks alone
npx adversarial-review --include-files --context-lines 20

# Just print the assembled prompt — no LLM call (pipe it anywhere you like)
npx adversarial-review --prompt-only > prompt.txt

# Machine-readable output for CI
npx adversarial-review --base main --json

# Diverse review: fan the same prompt out to two distinct provider families and
# gate on a quorum (diversity, not count — see "Multi-provider review")
npx adversarial-review --providers claude,gpt --quorum 1

# Autonomous convergence: review → fix → re-review until clean (working tree)
npx adversarial-review --loop --loop-unsafe

Example output

 NEEDS ATTENTION   working tree on branch main

Summary
  Two findings worth addressing before shipping: an unguarded secret assignment
  and a missing rate-limit on the new endpoint.

Coverage
  4 file(s) examined

Findings (2)

  CRITICAL [secrets] Hardcoded API key in environment helper  conf 0.95
    src/env.js:12-12
    The string literal assigned to `STRIPE_SECRET` is a live API key, not a
    placeholder. It will be committed to version control and included in the
    review payload sent to the model provider.
    ✗ failure: Any developer cloning the repo or any CI system gains full
      Stripe API access.
    → fix: Remove the key, rotate it immediately, and load from an env var or
      secret manager instead.

  MEDIUM [resource-exhaustion] /api/events returns unbounded results  conf 0.80
    src/routes/events.js:34-34
    The database query has no LIMIT clause. A single request can return every
    row in the events table.
    ✗ failure: A large events table causes the response to time out or OOM the
      process under normal traffic.
    → fix: Add pagination (LIMIT + OFFSET or cursor-based) and document the
      page-size cap in the API contract.

Next steps
  • Rotate the Stripe key immediately — treat it as compromised.
  • Add a LIMIT clause and pagination to the /api/events query.

Options

# Review target & output
--base <ref>           Review the current branch against <ref> (merge-base...HEAD).
--scope <mode>         auto (default) | working-tree | branch.
--input <file(s)>      Review artifact files (specs, tickets, rail-sets) instead of
                       a git diff. Repeatable + comma-separated. Cannot combine with
                       --base, --scope working-tree|branch, or --loop. See below.
--prompt-only          Print the assembled prompt to stdout and exit (no LLM call).
--json                 Print JSON (verdict matches the derived exit gate) instead of a rendered report.

# What gets sent
--max-files <n>        Inline-diff cutoff by changed-file count (default 50).
--max-bytes <n>        Inline-diff cutoff by diff size in bytes (default 262144).
--context-lines <n>    Diff context lines passed to git diff -U<n> (default 10).
--include-files        Also inline full post-change file contents (budgeted).
--allow-summary-review Allow API providers to review summary-only large diffs.
--allow-unsandboxed-cli Allow claude/agy/agent/copilot review without plan/read-only
                       mode. For opencode this DISABLES the generated read-only
                       config entirely — the reviewer gets your own agent's
                       write/bash access on an untrusted diff.
--allow-secrets        Send the payload even if the secret scan finds likely
                       credentials in the diff (off by default).

# Gate
--fail-on <severity>   Gate threshold: critical | high | medium (default) | low.
--min-confidence <x>   Findings below this confidence don't gate (default 0.5).
--fail-on-empty        Exit 1 (instead of 0) when there is nothing to review.

# Recall / precision
--verify               Second pass: drop a finding only with contradictory evidence.
--passes <n>           Run the review n times and merge findings (default 1).
--providers <list>     Multi-provider mode: fan the same review out to each family
                       token (e.g. gpt,gemini,claude) and merge with cross-provider
                       corroboration. "auto" picks >=2 distinct families. Diversity,
                       not count — distinct from --passes. Cannot combine with --provider.
--quorum <n>           needs-attention when >= n providers each flag a material
                       finding (default 1).

# Provider
--provider <name>      anthropic | openai | gemini | vercel | gateway |
                       cursor | agent | copilot | opencode | <local-cli-cmd>.
                       cursor/agent → Cursor Agent CLI; vercel/gateway → AI Gateway;
                       copilot → GitHub Copilot CLI; opencode → opencode (incl.
                       opencode-go models). copilot and opencode are explicit-only.
--model <name>         Force the model name (Gateway: use provider/model ids).
--api-base <url>       Override the active provider's API base URL.
--api-key <key>        Override the active provider's API key.
--headers <json>       Inject custom JSON headers into the LLM request.
--timeout <seconds>    Per-request API/CLI timeout (default 120).

# Reporting
--findings-ledger [path]
                       Append gating findings as JSONL to the ADLC findings ledger
                       (default .adlc/findings.jsonl) for P7 distillation.

# Loop mode (review → fix → repeat; see "Loop mode" below)
--loop                 Iterate review → fix → re-review until no gating findings
                       remain. Working-tree scope only. Composes with --providers.
--loop-max <n>         Max fix iterations (default 3): N fixes + a final review.
--loop-fixer <cmd>     Override the fixer CLI (default: auto-detect codex→claude→agy).
--loop-fixer-scope     sc2 (default): only finding-cited files. unrestricted: all files.
--loop-fixer-file-cap  Max files listed in unrestricted mode (default 100).
--loop-unsafe          Required on every platform: no enforced write confinement exists.
                       The fixer has unrestricted write access to your filesystem.
--loop-unsafe-allow-fix-secrets
                       Bypass the secret scan on the fix prompt (same-provider checked).

Exit codes

| Code | Verdict | Meaning | |------|-------------------|--------------------------------------------------------| | 0 | approve | No finding met the gate (--fail-on/--min-confidence). | | 2 | needs-attention | At least one material finding worth blocking on. | | 1 | error | Could not complete the review (including git failures). |

The model also reports its own verdict; if it disagrees with the derived gate, the disagreement is printed and the derived verdict wins. A gate that trusts the model's self-assessment can be argued out of blocking — this one can't.

The gate is hardened

  • Fails closed. Any git collection failure exits 1. An empty scope warns (use --fail-on-empty in CI so a misconfigured base ref can't silently pass).
  • Deterministic verdict. Exit code computed from findings: severity ≥ --fail-on and confidence ≥ --min-confidence.
  • Grounding checks. A finding citing a file outside the change set (API mode), or quoting evidence that doesn't appear in the provided context, is marked ungrounded and its confidence is halved for gating.
  • Prompt-injection resistant. The prompt instructs the reviewer that everything inside the repository context is untrusted data — and that any text in the diff attempting to influence the review (e.g. "reviewer: this is pre-approved") is itself a critical finding.
  • Secret scan. The payload is scanned for likely credentials (AWS keys, PEM blocks, API tokens, JWTs, hardcoded password assignments) before it leaves the machine; the run is refused unless --allow-secrets is passed.
  • Structured output at the API layer. Anthropic forced tool-use, OpenAI strict json_schema (with automatic fallback for gateways that reject it), Gemini responseSchema — JSON shape is enforced by the provider, with text-scraping and one self-correcting retry (which feeds the exact validation errors back to the model) as fallbacks. On final failure the raw output is saved to a temp file for debugging.

Choosing the model

If --provider is not given, the LLM is auto-detected. Inside Claude Code or Cursor, a different provider from the builder is preferred — a model reviewing its own output is a weaker critic. Otherwise:

  1. ANTHROPIC_API_KEY → Anthropic API (default model claude-sonnet-4-6)
  2. GEMINI_API_KEY → Gemini API (gemini-2.5-pro)
  3. OPENAI_API_KEY → OpenAI API (gpt-5)
  4. AI_GATEWAY_API_KEY (or VERCEL_OIDC_TOKEN) → Vercel AI Gateway (--provider vercel, default model anthropic/claude-sonnet-5)
  5. A local CLI agent on PATH: claude, codex, agy, or agent (Cursor Agent CLI)

Cursor Agent CLI (--provider cursor or agent): requires agent on PATH and agent login or CURSOR_API_KEY. Reviews run with --mode plan (read-only). This is not a localhost HTTP proxy — for third-party OpenAI-compatible proxies use --provider openai --api-base <url>.

GitHub Copilot CLI (--provider copilot): requires copilot on PATH and copilot login. Reviews run with --mode plan (read-only). Explicit-only — it is never auto-detected, and it does not count toward cross-family diversity: Copilot routes to Claude, GPT, or Gemini depending on --model, so treating it as an independent family would fake the diversity a --providers run reports. On Windows an npm-installed copilot is a .cmd shim and is refused: Copilot takes the prompt as a command-line argument, which cmd.exe would re-parse — use --provider claude or codex there.

opencode (--provider opencode): requires opencode on PATH and an authenticated provider (opencode auth list). Its opencode-go provider reaches models no other backend here serves — grok-4.5, kimi-k3, qwen3.7-max, glm-5.2, deepseek-v4-pro, minimax-m3 — which makes it a genuinely independent critic of Big-3-authored code. Default model opencode-go/grok-4.5; override with --model opencode-go/<name>. Explicit-only and, like copilot, not a diversity family.

opencode has no read-only flag, so reviews run under a generated config (supplied via OPENCODE_CONFIG) declaring a dedicated agent that can read the repository — read, grep, glob, list allowed — with edit, bash, task, webfetch, websearch, external_directory and the rest denied. Do not pass --agent plan yourself — opencode treats plan as a subagent and silently falls back to your default agent, which typically permits writes. --allow-unsandboxed-cli opts out and runs under your own config, with a warning.

--stream is ignored for opencode: under --format json every tool result is an event on stdout, and streaming mirrors those to stderr — which would put file contents the reviewer opened straight into a CI log. The review still returns normally; only the live progress output is suppressed.

Secrets are scanned on the way out as well as the way in. The pre-flight scan covers the outbound payload (the diff and its context). It cannot cover what a tool-using reviewer reads: with read/grep, a model can open a gitignored .env that was never in the diff and quote it into a finding. So the review response is scanned too, before it is rendered, printed as --json, or written to the findings ledger. Matches are masked in place and the redaction is announced on stderr.

Redaction is surgical rather than dropping the field, because "you committed a live key" is one of the most valuable things this tool reports — the finding survives with the credential masked. The scan is heuristic, so treat review output from a tool-enabled provider as sensitive regardless.

The permission block enumerates every key in opencode's schema, which matters for two non-obvious reasons: an unset key defaults to "ask", and an interactive prompt in a headless run blocks forever; and neither "*" nor write means what it looks like — "*" is not a wildcard, and modification is governed by edit, so denying write denies nothing. tools: { write: false } is not a control either. Verified against the real CLI: reads work, writes are blocked, shell commands are blocked.

Three further details are load-bearing, because OPENCODE_CONFIG merges with local config rather than replacing it — and that merge includes project-local opencode.json from the repository being reviewed:

  • The agent name is random per run. A repo that ships an opencode.json redefining the review agent by name wins the merge and re-enables write/bash. A fixed name is one the attacker knows; a per-run name cannot be written into a file in advance.
  • --pure is always passed, so a reviewed repo cannot load a plugin — plugins run code, which would make tool permissions moot.
  • A preflight runs before the diff is sent. opencode agent list must report our agent as primary in the merged config. This is checked up front because under --format json a silent downgrade to your default agent produces no output on any stream — there is nothing to detect afterwards.

Vercel AI Gateway (--provider vercel or gateway): one key, many provider/model ids (e.g. openai/gpt-5.6-sol, anthropic/claude-sonnet-5, google/gemini-2.5-pro). With only AI_GATEWAY_API_KEY set, --providers auto can fan across those families through the Gateway (native vendor keys still win when present). Family token anthropic (not the CLI-only token claude) selects the Anthropic family via Gateway/API.

Force any of them with --provider, and override the model with --model. Defaults are the strong tier of each provider — gate quality tracks model tier; downgrade with --model deliberately, not accidentally.

No key and no CLI agent? Use --prompt-only to emit the prompt and feed it to a model yourself.

Global config & resolution caching

Running the review repeatedly (a batch, a CI matrix, a loop) shouldn't re-walk the detection ladder — re-probing every CLI on PATH — each time. On the first auto-detect the resolved provider is cached in a global config file and reused on later runs:

$ADVERSARIAL_REVIEW_CONFIG                              # explicit override, else…
$XDG_CONFIG_HOME/adversarial-review/config.json         # else…
~/.config/adversarial-review/config.json
{
  "defaults": {
    "models": { "gemini": "gemini-2.5-pro", "openai": "gpt-5", "cli:agy": "gemini-3.1-pro-high" }
  },
  "cache": {
    "default": { "provider": "cli", "cliCmd": "agy", "family": "gemini", "model": null }
  }
}

This is strict JSON — no comments, no trailing commas (a syntax error makes the whole file ignored, though never overwritten). defaults.models pins the model per provider (precedence --model > pin > built-in default); a local CLI is keyed as cli:<cmd>. cache is auto-written, keyed on the builder context (claudecode/cursor/antigravity/default); its family is advisory — the diversity guard recomputes it from provider/cliCmd, so editing it by hand can't weaken the review.

Reuse is safe by construction, not blind trust:

  • Re-validated every run. A cached CLI must still be on PATH; a cached API provider must still have its key. Any miss falls through to a full re-detection (one PATH stat / env read — far cheaper than the ladder). This is the "unless the right path doesn't work, reprobe" fallback. A cached CLI additionally defers to any API/gateway provider that outranks local CLIs in the current context, so a stale CLI name can't shadow a safe API key that became available since the resolution was cached.
  • Diversity-preserving. The cache is keyed on the builder context, so a resolution made inside Claude Code can never be served to a Cursor or plain-shell run, and a resolution is never reused if its family is the builder's own family — the review stays adversarial.
  • No repository-local reviewer binary. A single trusted resolver is used by fresh detection, cache reuse, and the spawn itself: it canonicalizes the executable (resolving symlinks) and refuses any CLI that resolves inside the working tree — so a repo shipping a node_modules/.bin/claude shim (npm/npx put that dir on PATH) can be selected or executed by none of them. A cached CLI also records its canonical path and must still resolve to it, so a swapped/shadowed binary isn't reused.
  • Config can't come from the repo. The trust root is the whole git worktree (not just the cwd, so a repo-root config is refused even from a nested package). A config path is honored only when it is absolute and outside the worktree (symlinks resolved). An explicit ADVERSARIAL_REVIEW_CONFIG that is relative or inside the tree disables config (no read, no write) rather than silently falling back to your personal ~/.config; a relative XDG_CONFIG_HOME falls back to the home default. A repository you review can't supply model pins or cache entries.
  • Cached only after success — everywhere. A resolution is persisted only after a review completes successfully (in normal and --loop modes, per round); a cache-sourced provider that fails because its resolution is dead — a revoked/expired credential or a retired/invalid model — is invalidated and re-detected once with that provider excluded, so a stale entry can't stick. Transient failures (timeouts, 5xx) keep the cache.
  • Concurrency-safe. Cache writes reread-and-merge under a lock, so parallel batch/matrix runs sharing one config don't clobber each other's entries or a model-pin edit.

defaults.models is yours to author; cache is written automatically after the first successful run (silently). Delete the file to force a fresh detection.

Multi-provider review (--providers)

--passes samples one model N times; --providers fans the same review out to several independent providers and merges the results. The value is diversity, not count — a second model from a different family catches failure modes the first is blind to.

# Two distinct families, quorum 1 (any one provider's material finding gates)
npx adversarial-review --providers claude,gpt

# Let the tool pick >=2 distinct families for you (never the builder's own family)
npx adversarial-review --providers auto --quorum 2
  • Family tokens (gpt, claude, gemini, openai, anthropic, …) each resolve to the best reachable provider — the API when its key is present, otherwise the local CLI.
  • Merge + corroboration. Findings are merged by (file, category, overlapping lines); a finding raised by more than one provider is kept as one entry tagged with corroborated_by. Distinct findings at the same location are preserved, never collapsed.
  • Quorum verdict. The result is needs-attention when the number of providers that each raised a gating finding is ≥ --quorum (default 1); approve only when that count is 0.
  • No silent downgrade. If fewer providers are reachable than requested, the run emits a loud under-satisfaction notice and proceeds with what is available.
  • --providers cannot be combined with --provider (or --model).

Loop mode (--loop)

--loop runs an autonomous review → fix → re-review cycle until no gating finding remains (or a stop condition is hit). Each round reviews the working tree, hands the gating findings to a fixer CLI that edits the files, then re-reviews. It composes with --providers (each round is gated by the quorum verdict).

# Iterate until clean, capped at 3 fix rounds
npx adversarial-review --loop --loop-unsafe --loop-max 3
  • Working-tree scope only. The fixer writes to the working tree, so --loop is incompatible with --scope branch / --base. (Branch-scoped convergence is not yet supported.)
  • Fixer. Auto-detected in order codex → claude → agy; override with --loop-fixer.
  • Write Confinement. On Linux, kernel-level write confinement via bubblewrap (bwrap) restricts fixer writes strictly to the target workspace (blocking ~, credentials, parent directories, and other repositories). When bwrap is active, --loop runs securely without requiring --loop-unsafe. On macOS or Linux systems without bwrap/Landlock, --loop-unsafe is required to proceed.
  • Checkpoints. The working tree is stashed before each fix; on a fixer error or timeout the checkpoint is restored, and the recovery command is always printed.
  • Stop conditions (exit 2): no-progress (the gating set repeats), ceiling (--loop-max reached), no-diff (the fixer changed nothing), or fixer-error / fixer-timeout. A clean exit is 0.

Machine-readable loop output (--json)

With --json, the loop emits NDJSON events (loop_start, review, review_result, stash_created, fix, loop_end). The terminal line is a single consolidated loop_summary event carrying everything a run's evidence record needs:

{ "type": "loop_summary", "providers": ["claude", "gpt"], "iterations": 2,
  "verdict": "needs-attention", "exitReason": "ceiling",
  "survivingCount": 3, "acceptedCount": 0 }

verdict is derived from exitReason (cleanapprove); survivingCount is the gating findings still unresolved at exit; acceptedCount is always 0 (accepting a finding "with documented justification" is a human decision the loop leaves to you). It is copy-pastable straight into an ADLC P6 gate-manifest evidence entry:

adversarial-review --loop --json ... | jq -c 'select(.type=="loop_summary")'

Branch mode: pre-merge convergence (--loop --scope branch)

By default --loop reviews and fixes the working tree. With --scope branch (or --base <ref>) it drives a pre-merge convergence loop: review the feature branch against its base, commit each accepted fix onto the branch, and re-review until clean — the loop ADR-0007's 18-round Cursor case study had to run by hand.

# Converge the current branch against main, committing fixes as it goes
npx adversarial-review --loop --base main --loop-unsafe

Git safety — this only ever touches your feature branch:

  • The base ref (e.g. main) is read-only — resolved to a sha at loop start and only ever diffed. Every write (commit, reset --hard, clean) targets the feature branch's HEAD; the base is never a write target under any path.
  • A clean working tree is required at start: that guarantee is what makes the loop's git add -A / reset --hard / clean -fd safe — any file appearing mid-loop is fixer-created, so discarding it can never touch your own work.
  • Rollback: a failed/partial fix is reset --hard to the pre-fix commit; a no-progress/ceiling exit leaves the fix commits and prints a git reset --hard <original-HEAD> command to undo them all. SIGINT never auto-resets — it prints the recovery command and exits.
  • Fix commits use --no-gpg-sign (disposable per-round commits you'll squash-merge; a signing prompt would otherwise hang the non-interactive loop).
  • A pushed branch is warned (a reset will diverge it from its remote — a force-push would be needed) but not refused. A detached HEAD is refused, and an explicit --scope working-tree always wins over --base.

Recording findings for later distillation (--findings-ledger)

--findings-ledger [path] appends each gating finding as a JSONL line to an ADLC findings ledger (default .adlc/findings.jsonl), so repeated review findings can later be distilled (P7) into permanent, deterministic defenses. A ledger write failure only warns — the verdict and exit code are the product, the ledger is a side effect.

Reviewing artifacts, not diffs (--input)

Adversarial review isn't only for code. --input <file(s)> reviews arbitrary artifacts — a spec, a ticket, a design doc, a declared set of rails/invariants — with the same schema, grounding, --verify, --providers/--quorum, --passes, --findings-ledger, --json, and --prompt-only machinery. Only the review target and the charter change: instead of "find the strongest reasons this diff shouldn't ship", the model hunts for ambiguity, missing or untestable acceptance criteria, unhandled failure modes, unaddressed trust-boundary/security concerns, internal contradictions, and unenforceable invariants.

# Review a spec before anyone writes code
npx adversarial-review --input docs/spec.md

# Review a ticket + its rail-set together, with two provider families
npx adversarial-review --input ticket.md,rails.md --providers claude,gpt

# Emit the artifact-review prompt to feed a model yourself (no LLM call)
npx adversarial-review --input spec.md --prompt-only

This makes design/spec review (and rail-set adequacy review) a first-class, schema-validated, recordable gate rather than an informal one-shot prompt.

  • Fail-closed. --input reviews exactly the files you name — there is no summary fallback and no silent skip. A missing, binary, or oversized file, or an artifact with no reviewable text, is an error (exit 1), never a review that "approves" having read nothing.
  • Per-file size cap. In this mode --max-bytes is the per-file cap (raise it to review a larger single artifact); there is no aggregate budget.
  • Mutually exclusive with the git-diff targets (--base, --scope working-tree|branch) and with --loop (an artifact has no working tree to fix).

How it decides what to send

  • Small change (≤ --max-files files and--max-bytes diff bytes): the full diff (at -U<--context-lines>) is inlined and used as primary evidence. Add --include-files to inline the full post-change contents of changed files too (budgeted at 4× --max-bytes), which materially improves API-model review quality.
  • Large change: only a summary (status, shortstat, file list) is inlined, and the model is told to inspect the diff itself with read-only git commands — useful when the model has shell/tool access.

For API providers, summary-only large-diff reviews fail closed by default because the model cannot inspect your local repository. Use a local CLI provider, raise the inline limits, narrow the scope, or pass --allow-summary-review if you intentionally want an API model to review only the summary.

Privacy: whatever is collected is sent to the configured model provider. Treat the review payload as leaving your machine.

CI example

# .github/workflows/review.yml
- run: |
    set -o pipefail
    npx adversarial-review --base "origin/${{ github.base_ref }}" \
      --fail-on high --fail-on-empty --json | tee review.json
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
# Exit code 2 fails the job when a finding meets the gate.
# set -o pipefail ensures the exit code from adversarial-review is not swallowed by tee.
# --fail-on-empty guards against a misconfigured base ref silently passing.
# Note: use actions/checkout with fetch-depth: 0 so merge-base resolution works.

Project layout

| Path | Purpose | |--------------------------|----------------------------------------------------------------| | bin/cli.js | CLI entry point: secret gate, grounding, deterministic verdict. | | src/git-context.js | Collects git status + diffs (fail-closed), inline/summary rule. | | src/artifact-context.js| Collects --input artifact files (fail-closed) for artifact review. | | src/review.js | Prompt build, run/verify/multi-pass, multi-provider merge, quorum verdict, rendering. | | src/loop.js | --loop orchestration: fixer spawn, stash checkpoints, NDJSON events. | | src/llm.js | Provider config + structured-output call wrapper (API and CLI). | | src/schema-validate.js | Minimal JSON Schema walker + provider schema sanitizer. | | src/secrets.js | Outbound payload secret scan. | | src/findings-ledger.js | Appends gating findings to the ADLC findings ledger (--findings-ledger). | | src/utils.js | Arg parsing, logging, help text. | | prompt-template.md | The diff-review prompt (4 placeholders). | | prompt-template-artifact.md | The --input artifact-review prompt (same placeholders/schema). | | schema.json | JSON Schema the model output must conform to. |

prompt-template.md and schema.json are plain assets — edit them to tune the review or the output contract without touching code. The runtime validates against schema.json itself, so schema edits really do change the enforced contract. npm run sync-skill copies the review prompts (prompt-template.md, prompt-template-artifact.md) and schema.json into both skill trees (skills/ and .agents/skills/); a test fails if they drift.

Changelog

See CHANGELOG.md for release history.

License

Apache License, Version 2.0 © Chris Williams (@voodootikigod). See LICENSE and NOTICE.