Metadata-Version: 2.4
Name: contrarian
Version: 0.3.0
Summary: MCP server adding an independent second LLM perspective (Judge B) with full project graph context
Author-email: Anthony Micho <anthony.micho76@gmail.com>
License-Expression: AGPL-3.0-or-later
Project-URL: Repository, https://github.com/anthonymicho/contrarian
Keywords: mcp,llm,code-review,claude,judge
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp
Requires-Dist: openai>=1.0
Requires-Dist: python-dotenv
Requires-Dist: networkx>=3.4
Requires-Dist: graphifyy[leiden]
Requires-Dist: json-repair>=0.6
Provides-Extra: all
Requires-Dist: graphifyy[all]; extra == "all"
Dynamic: license-file

# Contrarian

MCP server that adds a second LLM perspective (Judge B) directly inside Claude Code.

Claude Code is Judge A — it already has full project context. Contrarian adds Judge B, an independent model from a different lab, via a single tool call. Same rubric, different priors, no context switch.

---

## How it works

1. Claude Code invokes `contrarian_review()` — no args for auto-detect, or pass specific files.
2. Contrarian resolves the input: git diff, last commit, or full audit (in that order).
3. Judge B (external model) reviews against `JUDGE.md` in the project root.
4. Findings are returned inline and appended to `REPORT.md` for the dialogue log.

Auto-detect order:
- **diff** — any staged/unstaged changes or untracked files
- **last-commit** — clean tree → reviews the last commit
- **audit** — forced with `audit=true`, or nothing else found

---

## Installation

Requires Python 3.10+ and `git` (auto-detect mode uses `git diff`/`git log`; without a git repo, Contrarian falls back to a full audit). You'll need a free API key from [Google AI Studio](https://aistudio.google.com) (no credit card required).

```bash
pip install contrarian
contrarian setup
```

`contrarian setup` prompts for your API key, then writes the MCP server config to `~/.claude/.claude.json` automatically. Restart Claude Code after running it.

That's it.

---

### Manual config (alternative)

If you prefer to configure manually, add to `~/.claude/.claude.json`:

```json
{
  "mcpServers": {
    "contrarian": {
      "type": "stdio",
      "command": "contrarian",
      "args": [],
      "env": {
        "JUDGE_B_API_KEY": "AI...",
        "JUDGE_B_BASE_URL": "https://generativelanguage.googleapis.com/v1beta/openai/"
      }
    }
  }
}
```

Default provider is Gemini (`generativelanguage.googleapis.com`, model `gemini-2.5-pro`). Change `JUDGE_B_BASE_URL` and `JUDGE_B_MODEL` to use any OpenAI-compatible provider (DeepSeek, Groq, Ollama, OpenRouter, etc.).

Anthropic models are blocked at runtime — Judge B must come from a different lab than Judge A.

### Provider switching

Judge B has two kinds of provider:

**API providers** (per-token billing) — any OpenAI-compatible endpoint, selected by `JUDGE_B_BASE_URL` + `JUDGE_B_MODEL` (or `contrarian use <preset>`). `JUDGE_B_API_KEY` required.

**CLI-agent providers** (flat subscription cost) — drive a local agent CLI instead of an API. No API key, no per-token cost. Two providers:

- **`cli:codex`** — runs Judge B on your **Codex subscription** via `codex exec`, **agentic**: read-only inside the project, so it reads files beyond the diff and catches cross-file ripple effects the diff-only API path misses. Optional model pin: `cli:codex:<model>` (e.g. `cli:codex:gpt-5.2`).
- **`cli:agy`** — runs Judge B on **Antigravity** (`agy`), a **different lab** (Gemini). Isolated (not agentic): runs sandboxed from a throwaway workspace and sees only the inline diff. Optional model pin: `cli:agy:<model>` (e.g. `cli:agy:gemini-3.1-pro-high`).

```
JUDGE_B_MODEL=cli:codex                       # persistent: all reviews via Codex subscription
contrarian_review({ model: "cli:agy" })       # per-call override
```

**Fan out to several models at once** — one review, N independent opinions, never merged:

```
JUDGE_B_MODEL="cli:codex+cli:agy"                          # persistent multi-model
contrarian_review({ models: ["cli:codex", "cli:agy"] })    # per-call
```

Each model's review is returned as a separate labelled block; you remain the supreme judge (no reconciliation, no auto-merge). One provider failing (timeout, outage, auth error — any reason) shows as a `FAILED` block and doesn't sink the others; if OpenAI is down, a `cli:codex+cli:agy` fan-out still returns agy's review. `JUDGE_B_CLI_TIMEOUT` (default 300s) bounds each CLI call.

**Security invariant:** a Judge never mutates the code it judges. codex is forced `--sandbox read-only` (non-configurable, runtime-guarded); agy runs sandboxed from a throwaway cwd where headless mode auto-denies all file tools. Independence is preserved — Judge B is always a different lab than Judge A (Claude), and an explicit adversarial preamble overrides the agent CLI's own collaboration framing (verified by a differential: same diff, project vs neutral cwd, no softening of verdict or core findings).

---

## Usage

Inside any Claude Code session, the tool is available as `contrarian_review`.

```
contrarian_review()                                    # auto-detect mode
contrarian_review({ path: "src/auth.py" })             # single file, diff
contrarian_review({ path: "src/auth.py", full: true }) # full file
contrarian_review({ paths: ["src/a.py", "src/b.py"] }) # multi-file, reviewed as a unit
contrarian_review({ audit: true })                     # force full repo walk
contrarian_review({ model: "google/gemini-3.1-pro-preview" }) # one-off model override
```

---

## Rubric

Place a `JUDGE.md` at the project root to customize what Judge B looks for. Without it, a built-in fallback rubric applies.

The built-in rubric covers four dimensions:

| Dimension | What to find |
|---|---|
| **Exactitude** | Logic errors, wrong assumptions, incorrect implementation |
| **Missing** | Unhandled cases, absent validation, unconsidered implications |
| **Premises** | Assumptions that could be wrong |
| **Alternatives** | Fundamentally different approaches |

Output is always JSON:

```json
{
  "exactitude": { "verdict": "ok|warn|fail", "findings": [] },
  "missing": [],
  "premises": [],
  "alternatives": []
}
```

---

## Report log

Findings are appended to `REPORT.md` at the project root under `[Judge B]` blocks. Claude Code annotates findings under `[Judge A]` blocks. Judge B reads previous exchanges before each review — closed findings (`addressed`, `won't fix`, `disagree`) are not re-raised.

Run `contrarian stats` from the project root for a summary: review count, model breakdown, exactitude verdicts, and finding totals parsed from `REPORT.md`.

---

## Phase roadmap

- **Phase 2:** MCP server + project graph context. Auto-detect mode. Python rewrite.
- **Phase 3:** Operational hardening — token budget guard, graph latency, detached HEAD handling.
- **Phase 4 (current):** PyPI distribution, CLI provider switching (`setup`/`model`/`use`), usage stats (`contrarian stats`). Next: agent watchdog — autonomous background review on commit.

---

## License

Dual-licensed: [AGPL-3.0-or-later](LICENSE), with a commercial license available — see [LICENSING.md](LICENSING.md). Versions ≤ 0.2.4 remain MIT.
