Skip to main content

aeview

aeview fans a change across multiple reviewers and agent harnesses, then merges one deduplicated verdict

Fan code reviewers across multiple AI agent harnesses, then merge one deduplicated verdict.

aeview runs the same change past several reviewers — each a prompt you check into your repo — across several agent harnesses (Claude Code, Codex, Copilot, pi), in parallel. It collects every finding, deduplicates them with an LLM judge, and writes a single report.json plus an exit code you can loop on: 0 approve · 1 needs-attention · 2 error.

The bet: a reviewer is a versioned artifact (a prompt + the harnesses it runs on), and a panel of independent models catches what any single model misses.

# install the aeview-install skill, then run /aeview-install in your agent to set up the CLI:
npx skills add MarlzRana/aeview --skill aeview-install --global
cd your-repo
aeview run          # review your current changes (auto mode); exit 1 if anything needs attention

Contents


How it works

A reviewer is a prompt (REVIEWER.md) plus a set of harness instances (an agent + a model). One aeview run does this:

  1. Resolve reviewers — walk up from your cwd to home collecting .aeview/reviewers/*.
  2. Resolve the scope — turn --scope into a concrete git diff (working tree, a branch, a PR, …).
  3. Bundle the diff — small diffs are frozen inline; large ones hand the agent read-only git to self-collect, so nothing is silently truncated.
  4. Fan out — run every reviewer × harness pair concurrently, each in a read-only sandbox (read anywhere, write nothing). Each emits findings against a fixed schema.
  5. Dedupe & merge — an LLM judge groups duplicate findings across the panel; survivors are kept verbatim with their provenance and an agreement count. See Deduplication.
  6. Report — write report.json and exit 0 / 1 / 2.

Everything is persisted under ~/.aeview/runs/<id>/, so a killed run can be resumed.

Requirements

  • Python 3.14+ (the uv installer can fetch one for you).
  • macOS or Linux.
  • Harness auth. Claude Code, Codex, and Copilot are driven through their Python SDKs, which bundle a pinned CLI binary — you don't install those CLIs separately. The pi harness is PATH-gated: install pi and srt (npm i -g @anthropic-ai/sandbox-runtime) yourself. You need to be authenticated with each harness a reviewer uses. Run aeview doctor to see exactly what's missing for the reviewers you have.
  • gh (GitHub CLI) — for --scope pr, for --post-comments (posting the review onto the PR), and to auto-detect a branch's base from its open PR. Optional otherwise: aeview falls back to origin/HEAD, then main/master/trunk, when gh or a PR isn't available.

Install

Install the aeview-install skill, then run it — it installs the aeview CLI, installs the reviewer skills, and verifies your setup:

npx skills add MarlzRana/aeview --skill aeview-install --global

Then run /aeview-install in your agent (Claude Code, …). Later, run /aeview-update to upgrade the CLI and skills to the latest release.

Prefer to install by hand?
uv tool install aeview   # recommended; also fetches Python 3.14
# or
pipx install aeview

After install, sanity-check your setup:

aeview doctor

Quickstart

cd your-repo

# Review your changes (auto mode: uncommitted work if the tree is dirty, else your branch vs base):
aeview run

# Preview the plan (roster + scope + bundle size) without spending anything:
aeview run --dry-run

# Review a branch against its base, with every reviewer you have:
aeview run --scope branch --reviewers all

# Get machine-readable output and act on the exit code:
aeview run --json && echo "approved" || echo "needs attention (or error)"

A run prints a human summary (or --json), writes report.json, and exits with the verdict code.

Scopes — what to review

--scope <type>[:value]. Omit --scope entirely for auto. Where a type takes a value, the "bare" form (no :value) uses the default shown:

Scope Bare default Reviews
working-tree — all uncommitted changes
staged — only the staged changes
branch[:base] base = auto your branch vs base
pr[:number] current branch's PR a PR's diff (via gh)
effective-pr[:base] base = auto branch commits + uncommitted work, vs base
commits[:a,b,c] HEAD exactly the commits listed, each vs its own parent
range:A..B value required the diff between A and B
patch:file value required a diff file (or - for stdin)
auto — dirty → working-tree, else your branch vs base

Notes:

  • commits is a set, not a range: --scope commits:a,c,e reviews exactly those three commits, each shown against its own parent (a union of per-commit patches). A single commit is just the one-element case; bare commits is HEAD.
  • --include-dirty folds uncommitted work onto a committed scope — use it with branch, auto, or staged; it's a no-op on working-tree.
  • --allow-conflicts reviews despite an in-progress merge/rebase (refused by default).
  • Base resolution order: explicit value → the PR base → origin/HEAD → main/master/trunk.

Posting the review to a PR

With --scope pr, add --post-comments to publish the merged review onto the PR on GitHub (via gh). aeview posts one review per run — a GitHub review with event COMMENT, so it never approves or requests changes and can't gate your merge — made of:

  • a summary comment (verdict, summary, coverage), and
  • one inline comment per finding, anchored to its file and line in the PR diff. A finding whose line isn't part of the diff (reviewers can read your whole tree) is listed in the summary instead, so nothing is dropped.

Findings on the same line are combined into one comment; each run posts a fresh review, so re-running across a loop leaves a thread-per-finding audit trail on the PR. A clean run still posts a short "approved — no findings" note. Every comment is authored by your gh account (aeview has no separate identity), so it carries a visible aeview (automated review panel) badge and names the reviewer(s) behind each finding. --post-comments requires an open PR and authenticated gh, and it's an error to pass it with any scope other than pr.

Reviewers

A reviewer lives at .aeview/reviewers/<name>/REVIEWER.md: YAML frontmatter + a prompt body.

---
name: security
description: Hunts auth, injection, and trust-boundary bugs.
harnesses:
  - { harness: claude-code, model: claude-opus-4-8 }
  - { harness: codex, model: gpt-5.5, thinking: xhigh }
  - { harness: copilot, model: gemini-3.1-pro-preview }
  - { harness: pi, model: xai/grok-4.6 }
auto-activate-paths:
  - "src/api/**"
---

You are a security reviewer. Find the injection, the auth gap, the unsafe
deserialization... (the rest of the prompt)

Frontmatter fields:

Field Required Meaning
name yes Must equal the directory name.
description no One line, shown by aeview reviewers.
harnesses no List of { harness, model, thinking? }. Omit to use the global fallbackReviewerHarnesses.
auto-activate-paths no Globs that opt this reviewer into auto mode.
custom-schemas no Reshape or drop a finding's descriptive slots. See Customizing the finding schema.

Resolution (walk-up)

aeview climbs from your cwd through its parent directories up to your home directory, looking for <dir>/.aeview/reviewers/<name>/REVIEWER.md. First match wins, so a repo-local reviewer shadows a personal one of the same name. Your home ~/.aeview/reviewers/ holds reviewers available everywhere — including the seeded default reviewer (an adversarial general-purpose reviewer).

Selecting reviewers

aeview run --reviewers security              # one
aeview run --reviewers security,tests        # several (comma-separated)
aeview run --reviewers security --reviewers tests   # or repeated
aeview run --reviewers all                   # every reviewer visible here

With no --reviewers, aeview uses auto mode.

Resource references

The reviewer's own directory (absolute path) is prepended to its prompt, so you can drop supporting files beside REVIEWER.md (references/checklist.md, scripts, …) and reference them with relative paths. Reviewers can read anywhere, so the links resolve.

Customizing the finding schema

Every finding has a fixed skeleton — severity, confidence, and location — that the merge, sort, verdict, and dedup logic depend on. Those are never customizable. The four descriptive slots — title, body, recommendation, category — are, via custom-schemas: point a slot at a JSON Schema fragment (inline, or a path to a .json file relative to the reviewer dir) to reshape it, or set it to null to drop it. It's your reviewer's call whether a drop makes sense.

custom-schemas:
  category: { type: string, enum: [perf, a11y, docs] }   # your own taxonomy (inline)
  body: ./schemas/rubric.json                            # body becomes a structured rubric object
  recommendation: null                                   # drop the slot entirely
  • Each harness's output is validated against the composed schema (a real JSON-Schema check), including your fragment's inner constraints — a finding that doesn't conform fails the review loudly rather than landing malformed in the report. A fragment that isn't a valid JSON Schema is rejected up front, when the reviewer resolves.
  • Optional sub-fields in a fragment are expressed the OpenAI-strict way — nullable and still listed in required (e.g. { "type": ["string", "null"] }) — so they behave the same across all four harnesses. Omitting a field from required makes it truly optional on claude/copilot/pi but the codex harness will still require it.
  • The composed schema is frozen with the run, so aeview resume validates against the exact shape the reviewer started with (never a since-edited REVIEWER.md).
  • Dedup keeps the survivor verbatim: when a reviewer's custom finding merges with another, only the survivor's slots are kept — a merged-away finding's custom fields (e.g. its rubric scores) are not aggregated. $ref is not supported in a fragment (inline the definition).

Scaffolding

aeview init security                 # create .aeview/reviewers/security/REVIEWER.md
aeview init security --with-harness  # also scaffold a harnesses: block

Auto mode, activation & .aeviewignore

Auto mode (no --reviewers) builds the roster as:

default (always) ∪ every reviewer whose auto-activate-paths matches a changed file.

A reviewer with no auto-activate-paths never auto-runs (select it by name or with all). Matching uses literal globs (Python's PurePath.full_match), anchored at the reviewer's .aeview parent directory: a single * stops at /, ** crosses directories, there's no negation, and it's case-sensitive. So write backend/**, not backend/.

Auto-activation needs repo-root-relative paths, so --scope patch (and running outside a git repo) runs only default — name any extra reviewers explicitly there.

.aeviewignore filters files out of the diff before it's reviewed — handy for lockfiles, generated code, vendored trees. It uses faithful gitignore semantics (the pathspec library):

uv.lock
dist/
**/__snapshots__/
!important.generated.ts

Files are collected on the same cwd→home walk (your home file is ~/.aeviewignore); each file's patterns anchor at its own directory, nearest rules win, and ! negation is supported.

Like auto-activation, .aeviewignore is skipped for --scope patch (its paths aren't repo-root-relative) — a patch is reviewed exactly as supplied.

Harnesses

A harness instance is { harness, model, thinking? }. Supported harnesses:

harness Driven via Example model
claude-code claude-agent-sdk claude-opus-4-8
codex openai-codex gpt-5.5 (e.g. thinking: xhigh)
copilot github-copilot-sdk a Copilot-served model
pi pi CLI + srt (not bundled) xai/grok-4.6 (e.g. thinking: high)

Every harness runs read-anywhere, write-nowhere: a reviewer can read any file (to gather context) but cannot modify your repo. Claude Code and Codex enforce this with their native read-only sandboxes; Copilot uses a deny-by-default permission handler that only approves reads; pi wraps the whole process in Anthropic Sandbox Runtime (srt), with a write hole only for that review's session file.

Claude / Codex / Copilot ship a pinned binary, so aeview is insulated from your own CLI upgrades. pi uses whatever pi (and srt) are on PATH. To point a harness at a different binary, set overrideHarnessBinaries.

Deduplication

A panel of independent reviewers will report the same issue more than once. After the fan-out, aeview pools every finding and hands the pool to a single dedup judge — the harness named by deduplicationHarness — whose job is deliberately narrow: it only groups.

  • It never rewrites, re-scores, or summarizes a finding. Per group it just names one survivor (kept verbatim) and the duplicate ids merged into it; anything it doesn't mention stays as its own group of one.
  • Precision over recall. It's instructed to merge only when confident, because a wrong merge hides a distinct problem while a wrong split only shows two views of one issue — the cheaper mistake. When in doubt, findings stay separate.
  • "Same issue" means same root cause at substantially the same location — not merely the same file, category, or severity.

Each survivor becomes one finding in the report carrying its whole group's provenance: agreement (the group size) and sources (one entry per merged finding, naming the review it came from). Findings are handed to the judge as untrusted data, not instructions, so a crafted finding can't steer it into over-merging (and silently hiding real issues).

Customizing the judge

The judge's full instructions live in a DEDUPLICATION.md prompt file, discovered by the same walk-up as reviewers: aeview climbs from your cwd to home looking for <dir>/.aeview/DEDUPLICATION.md, and first match wins. So a repo can check in .aeview/DEDUPLICATION.md to tailor grouping to its domain — e.g. loosen the code-centric "same root cause at the same location" wording for a docs or LLM-wiki panel — and it shadows your personal one. Your home ~/.aeview/DEDUPLICATION.md is seeded on first run (write-if-absent, so edits are never clobbered) and is the always-present fallback when nothing nearer is found. Any YAML frontmatter is stripped before it's sent to the harness.

The resolved prompt is frozen into the run at start (dedup/DEDUPLICATION.md) and read back at merge time, so a resume re-groups against the exact bytes the run began with — never a since-edited file. aeview doctor reports which DEDUPLICATION.md resolves from your cwd, and aeview run --dry-run previews it. (Leaving deduplicationHarness unset leaves findings ungrouped — but aeview reports that as a failed dedup with a warning, not a silent off-switch; see below.)

When dedup is skipped or fails

The panel's findings are never discarded — if the judge doesn't run or errors, they pass through as a raw union (one group each). dedup.status in the report records what happened:

dedup.status When Findings
ok the judge ran and grouped merged (deduplicated)
skipped only one review contributed, or only one finding total — nothing could be a duplicate raw union, no billed call
failed the judge errored/timed out, or no deduplicationHarness is configured raw union + a loud warning that duplicates were not removed; resume re-runs it

Configuration

Global settings live in ~/.aeview/settings.json, seeded (write-if-absent) on first run. Its keys are camelCase; the JSON run artifacts (report.json, review.json) use snake_case.

{
  "fallbackReviewerHarnesses": [
    { "harness": "claude-code", "model": "claude-opus-4-8" }
  ],
  "deduplicationHarness": { "harness": "claude-code", "model": "claude-opus-4-8" },
  "retention": { "keepLast": 20, "ttlDays": 14 },
  "reviewTimeoutSeconds": 1200,
  "overrideHarnessBinaries": { "codex": "/usr/local/bin/codex" },
  "harnessSettings": {
    "pi": {
      "extraSandboxAllowedDomains": ["my-proxy.internal"]
    }
  }
}
Key Meaning
fallbackReviewerHarnesses Harnesses a reviewer runs on when its REVIEWER.md has no harnesses: block.
deduplicationHarness The harness used to deduplicate findings across the panel.
retention Auto-prune old runs: keep at least the newest keepLast, and drop runs that are also older than ttlDays days.
reviewTimeoutSeconds Timeout (seconds) per harness attempt; transient errors retry, so a review's total time can exceed it. A timed-out review fails fast (no retry); resume re-runs it.
overrideHarnessBinaries Optional per-harness override of the CLI binary, by path. Keys: claude-code, codex, copilot, pi.
harnessSettings.pi Pi-only sandbox knobs. extraSandboxAllowedDomains is unioned onto the built-in model-API allowlist; onlySandboxAllowedDomains (when set) replaces that list and ignores extra.

Commands

Command What it does
aeview run Run reviewers over a scope; print the gate (verdict + findings, see below) and exit 0/1/2.
aeview status [run-id] Per-review progress + coverage (defaults to the latest run). --wait blocks to a terminal state and adopts its exit code.
aeview result [run-id] Print a finished run's full report; exit with its 0/1/2 code.
aeview resume <run-id> Re-run a run's non-done reviews against its frozen bundle, then re-merge.
aeview list Recent runs, newest first: id, time, scope, verdict, coverage.
aeview reviewers [name] List the reviewers visible here (walk-up), or show one's detail.
aeview init <name> Scaffold a repo reviewer. --with-harness adds a harnesses: block.
aeview doctor Preflight: reviewer config, harness binaries + auth, and gh. Exits 1 on failure.
aeview version Print the version.

Common run flags: --scope, --reviewers, --include-dirty, --allow-conflicts, --post-comments (with --scope pr), --dry-run, --json. Most commands accept --json for machine-readable output.

The report & exit codes

The full merged artifact is report.json — what aeview result and the on-disk file give you:

{
  "verdict": "needs-attention",          // "approve" | "needs-attention"
  "summary": "...",
  "findings": [
    {
      "id": "f3",                          // stable run-local id assigned during merge
      "title": "Unvalidated path used in file read",
      "body": "...",
      "severity": "high",                // critical | high | medium | low
      "category": "security",            // bug | security | regression | test_gap | maintainability
      "confidence": 0.9,                 // 0.0–1.0
      "location": { "file": "src/api/files.py", "line_start": 42, "line_end": 48 },
      "recommendation": "...",
      "agreement": 2,                    // size of the dedup group (findings merged into this one)
      "sources": [                         // one entry per merged finding, with its originating review
        { "review": "security__codex-gpt-5.5", "severity": "high", "confidence": 0.9 },
        { "review": "security__claude-code-claude-opus-4-8", "severity": "high", "confidence": 0.85 }
      ]
    }
  ],
  "next_steps": [ { "source": "security__...", "steps": ["..."] } ],
  "coverage": { "contributed": 3, "failed": 0 },   // reviews that completed vs. reviews that failed
  "dedup": { "status": "ok", "harness": "...", "reason": null, "warning": null },
  "usage": { "reviews": {...}, "dedup": {...}, "total": { "input_tokens": 0, "output_tokens": 0, "cost_usd": 0.0 } }
}

The agreement count, the sources provenance, and the dedup block all come from Deduplication.

aeview run prints a gate, not the full report: it's report.json with a top-level run_id added and the result-only detail omitted — findings[].id, next_steps, usage, and the dedup fields other than status (and dedup is dropped entirely when the run had a single review — there's nothing to deduplicate). The kept fields keep their report.json names, so a consumer that reads only the gate's fields works against both run and result. The dropped detail (token/cost accounting, per-review next steps, dedup provenance, per-finding ids) lives in aeview result.

Exit codes (the loop-until-clean contract):

Code Meaning
0 approve — no actionable findings
1 needs-attention — at least one finding to act on
2 error — the run couldn't be trusted (e.g. every review failed)

Runs, lifecycle & output

Each run is a directory under ~/.aeview/runs/<id>/:

runs/<id>/
  run.json                               # the manifest: scope, roster, dedup plan, state, pid
  bundle/                                # the frozen diff under review
  reviewers/<reviewer>/<instance>/
    review.json                          # that review's findings + status + usage
    review.log                           # raw harness-SDK event stream (JSONL)
  dedup/DEDUPLICATION.md                 # the walk-up-resolved judge prompt, frozen at run start
  dedup/<instance>/result.json           # the dedup judge's grouping decision
  report.json                            # the merged verdict
  • Timeout & fail-fast. Each harness attempt is bounded by reviewTimeoutSeconds; a timed-out review fails fast (timeouts aren't retried). A failed review is recorded — the run continues — and resume re-runs it.
  • Crash recovery. Runs record their pid; a crashed run is reconciled to interrupted and can be resumed. aeview status --wait blocks until a run reaches a terminal state.
  • Retention. On each run, terminal runs outside the newest keepLast and older than ttlDays are pruned (keepLast is a guaranteed floor).

Development

git clone https://github.com/MarlzRana/aeview
cd aeview
uv sync                    # install deps (incl. dev group)
uv run pytest              # the offline test suite (no model calls)
uv run ruff check          # lint
uv run pyright             # type-check
uv run aeview --help

License

MIT

Metadata

Release files for aeview 0.0.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aeview 0.0.7
File Size Uploaded Provenance
aeview-0.0.7.tar.gz 108.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for aeview 0.0.7
File Interpreter ABI Platform
aeview-0.0.7-py3-none-any.whl Python 3 none any Details

Total release size: 228.4 kB

Release files / aeview-0.0.7.tar.gz

Download URL aeview-0.0.7.tar.gz
Size 108.4 kB
Tags Source
SHA-256 checksum
How to use checksums
52932beb1b11b0290be0fe6dd2170e17c26d13856f955e686b36d016117c640d
BLAKE2b-256 checksum
How to use checksums
e50a743bc712990cc883a0ad7be4d6ad18dd5f1178402881114cf7bfcde59eb5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / aeview-0.0.7-py3-none-any.whl

Download URL aeview-0.0.7-py3-none-any.whl
Size 120.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a3294b26a7f83dd15a5e1327d3b91bab6e7e1a88bcef339ddae0b749dde57d44
BLAKE2b-256 checksum
How to use checksums
7abdfa33cf6e6be22fc6b7e6ec00a8cf65dbcba7b75cb58d7af7e8c50f04d0d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Security for aeview 0.0.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for aeview-0.0.7.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page