Your AI writes code. ArbiterX decides if it's good enough.
The intelligent middleware that makes AI coding assistants write like senior engineers — minimal, robust, unbreakable.
Install • Quick Start • How It Works • Examples • Integrations • Full Guide
Your AI assistant just wrote this:
def get_user(id):
try:
conn = psycopg2.connect("postgresql://admin:password123@localhost/db")
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = " + str(id))
return cursor.fetchone()
except:
return None5 security vulnerabilities. 3 resource leaks. 0 type hints. Your code review just got longer.
With ArbiterX active, the AI writes this instead:
from typing import Optional
from dataclasses import dataclass
@dataclass(frozen=True)
class User:
id: int
name: str
email: str
def get_user(user_id: int, conn: Connection) -> Optional[User]:
"""Fetch a user by ID. Returns None if not found."""
row = conn.execute(
"SELECT id, name, email FROM users WHERE id = ?", (user_id,)
).fetchone()
return User(**row._asdict()) if row else NoneTyped. Parameterized. No hardcoded secrets. No resource leak. Ships on first review.
| 97.1% Token Reduction |
0.25s Map Build Time |
0.5ms Query Latency |
109 Tests Passing |
| 10 Engineering Rules |
6 Quality Checks |
22 Languages |
5 LLM Providers |
196,247 tokens → 5,639 tokens. That's what your AI actually needs to see.
pip install arbiterx-gateUsing Python 3 specifically:
pip3 install arbiterx-gatePermission issues? Install for your user:
pip install --user arbiterx-gateIn a virtual environment (recommended):
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install arbiterx-gateFrom source:
git clone https://github.com/NeelPrime/arbiterx.git
cd arbiterx
pip install -e .Verify:
arbiterx --version
# arbiterx 0.3.0Requirements: Python 3.9+ · No system dependencies · Works offline for local models
# Install
pip install arbiterx-gate
# Set up (once per project)
arbiterx init
arbiterx map # Index your codebase (30 seconds)
# Use
arbiterx route "task" # See how any task gets classified
arbiterx query Symbol # Find any function instantly
arbiterx gate --file main.py # Score any file 0-100That's it. You're running.
Using with an AI tool? After pip install arbiterx-gate:
# Claude Code
/plugin install NeelPrime/arbiterx
# Codex CLI
codex plugin install NeelPrime/arbiterx
↑ pip install arbiterx-gate && arbiterx init && arbiterx map
ArbiterX parses your code with tree-sitter, builds a graph of every function/class/import, ranks them with PageRank, and stores it in SQLite.
When the AI needs context, it queries the map:
- ❌ Without ArbiterX: Read 40 files → 200,000 tokens
- ✅ With ArbiterX: Query relevant signatures → 5,000 tokens
Every task gets classified:
| Task | Type | Model |
|---|---|---|
| "rename this variable" | TRIVIAL | Haiku / GPT-4o-mini |
| "fix the auth bug" | DEBUGGING | Sonnet / GPT-4o |
| "redesign the caching layer" | ARCHITECTURE | Opus / o1 |
Every AI output gets scored:
┌───────────────────────────────────────────┐
│ QUALITY GATE │
│ │
│ Security ····· ✓ No hardcoded secrets │
│ Robustness ··· ✓ Errors handled │
│ Efficiency ··· ✓ No O(n²) patterns │
│ Types ········ ✓ All functions typed │
│ Style ········ ✓ Clean naming │
│ Complete ····· ✓ No TODOs left │
│ │
│ Score: 100/100 ✓ PASSED │
└───────────────────────────────────────────┘
These run on every piece of generated code. No exceptions.
| # | Rule | What Gets Rejected |
|---|---|---|
| 1 | YAGNI | Building features nobody asked for |
| 2 | Error Handling | open("file") without try/except |
| 3 | Type Safety | def process(x, y): with no types |
| 4 | Resource Cleanup | f = open(...) without with |
| 5 | No Magic Numbers | if retries > 3: → name it |
| 6 | No Dead Code | Commented-out code, unused imports |
| 7 | Single Responsibility | 50-line functions that do 3 things |
| 8 | Fail Fast | Validating inputs on line 47 |
| 9 | Idempotency | Unsafe-to-retry operations |
| 10 | Performance | Nested loops when unnecessary |
Install the package and plugin, then start coding. ArbiterX scores all generated code automatically.
> pip install arbiterx-gate
> /plugin install NeelPrime/arbiterx
✓ ArbiterX installed. Engineering mode active.
✓ Quality gate will score all generated code.
> Build a REST endpoint to create users
Claude (with ArbiterX active):
from typing import Annotated
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel, EmailStr
router = APIRouter()
class CreateUserRequest(BaseModel):
name: str
email: EmailStr
class UserResponse(BaseModel):
id: int
name: str
email: str
@router.post("/users", response_model=UserResponse, status_code=201)
async def create_user(
request: CreateUserRequest,
db: Annotated[Database, Depends(get_db)],
) -> UserResponse:
"""Create a new user. Returns 409 if email already exists."""
existing = await db.execute(
"SELECT id FROM users WHERE email = ?", (request.email,)
).fetchone()
if existing:
raise HTTPException(status_code=409, detail="Email already registered")
result = await db.execute(
"INSERT INTO users (name, email) VALUES (?, ?) RETURNING id",
(request.name, request.email),
)
user_id = result.fetchone()["id"]
return UserResponse(id=user_id, name=request.name, email=request.email)What ArbiterX enforced: Typed params ✓ | Parameterized SQL ✓ | Proper HTTP codes ✓ | No magic strings ✓ | Validation via Pydantic ✓ | Error case handled ✓
> pip install arbiterx-gate
> codex plugin install NeelPrime/arbiterx
> codex
You: Write a file downloader with retry logic
Codex (with ArbiterX active):
from pathlib import Path
from typing import Optional
import httpx
MAX_RETRIES = 3
DOWNLOAD_TIMEOUT = 30
CHUNK_SIZE = 8192
class DownloadError(Exception):
"""Raised when a download fails after all retries."""
def download_file(
url: str,
destination: Path,
retries: int = MAX_RETRIES,
) -> Path:
"""Download a file with retry logic. Returns the saved path."""
if not url.startswith(("http://", "https://")):
raise ValueError(f"Invalid URL scheme: {url}")
last_error: Optional[Exception] = None
for attempt in range(1, retries + 1):
try:
with httpx.stream("GET", url, timeout=DOWNLOAD_TIMEOUT) as response:
response.raise_for_status()
with open(destination, "wb") as out_file:
for chunk in response.iter_bytes(CHUNK_SIZE):
out_file.write(chunk)
return destination
except httpx.HTTPError as err:
last_error = err
if attempt == retries:
break
raise DownloadError(
f"Failed after {retries} attempts: {last_error}"
) from last_errorWhat ArbiterX enforced: Named constants ✓ | Type hints ✓ | Input validation (fail fast) ✓ | Error handling with retries ✓ | Context manager for file ✓ | Custom exception ✓ | No bare except ✓
Drop .cursor/rules/arbiterx.mdc into your project. Now every Cursor generation follows the rules.
You (in Cursor): Add caching to the database queries
Cursor (with ArbiterX rules):
from functools import lru_cache
from typing import Optional
from dataclasses import dataclass
from time import monotonic
CACHE_TTL_SECONDS = 300
MAX_CACHE_SIZE = 128
@dataclass(frozen=True)
class CacheEntry:
value: object
expires_at: float
class QueryCache:
"""Simple TTL cache for database query results."""
def __init__(self, ttl: int = CACHE_TTL_SECONDS, max_size: int = MAX_CACHE_SIZE) -> None:
self._store: dict[str, CacheEntry] = {}
self._ttl = ttl
self._max_size = max_size
def get(self, key: str) -> Optional[object]:
"""Get a cached value. Returns None if missing or expired."""
entry = self._store.get(key)
if entry is None:
return None
if monotonic() > entry.expires_at:
del self._store[key]
return None
return entry.value
def set(self, key: str, value: object) -> None:
"""Store a value with TTL. Evicts oldest if at capacity."""
if len(self._store) >= self._max_size:
oldest_key = next(iter(self._store))
del self._store[oldest_key]
self._store[key] = CacheEntry(value=value, expires_at=monotonic() + self._ttl)What ArbiterX enforced: Frozen dataclass (immutable) ✓ | Named constants ✓ | Typed everything ✓ | Docstrings ✓ | Single responsibility ✓ | No over-engineering (stdlib only) ✓
from arbiterx.gate import QualityGate
from arbiterx.ladder.interrogator import SelfInterrogator
# Check any code snippet
gate = QualityGate()
enforcer = SelfInterrogator()
code = """
def send_email(to, subject, body):
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.login('[email protected]', 'password123')
server.sendmail('[email protected]', to, body)
"""
# Quality Gate
result = gate.validate(code, "python")
print(f"Score: {result.score}/100") # 25/100
print(f"Passed: {result.passed}") # False
for issue in result.issues:
print(f" ❌ [{issue.severity}] {issue.message}")
# Output:
# ❌ [high] Hardcoded password detected
# ❌ [medium] No error handling for network operation
# ❌ [low] Single-letter variable namesThe fix (what ArbiterX would guide the AI to write):
import smtplib
import os
from typing import Optional
from dataclasses import dataclass
SMTP_HOST = "smtp.gmail.com"
SMTP_PORT = 587
@dataclass(frozen=True)
class EmailConfig:
host: str = SMTP_HOST
port: int = SMTP_PORT
username: str = ""
password: str = ""
def send_email(
recipient: str,
subject: str,
body: str,
config: Optional[EmailConfig] = None,
) -> bool:
"""Send an email. Returns True on success, False on failure."""
if config is None:
config = EmailConfig(
username=os.environ.get("SMTP_USER", ""),
password=os.environ.get("SMTP_PASS", ""),
)
if not config.username or not config.password:
raise ValueError("SMTP credentials not configured")
try:
with smtplib.SMTP(config.host, config.port) as server:
server.starttls()
server.login(config.username, config.password)
server.sendmail(config.username, recipient, body)
return True
except smtplib.SMTPException as err:
raise RuntimeError(f"Failed to send email: {err}") from errScore: 100/100 ✓ — Secrets from env ✓ | Context manager ✓ | Error handling ✓ | Typed ✓ | Named constants ✓ | Input validation ✓
$ arbiterx route "rename the getUserName function to getUsername"
Task rename the getUserName function to getUsername
Type REFACTORING
Complexity TRIVIAL
Context Scope FILE
Latency INTERACTIVE
Est. Tokens 50
→ Model: haiku (cheapest — this is a trivial rename)
$ arbiterx route "redesign the authentication system to support OAuth2 and SAML"
Task redesign the authentication system to support OAuth2 and SAML
Type ARCHITECTURE
Complexity EXPERT
Context Scope REPO
Latency BATCH
Est. Tokens 4000
→ Model: opus (needs deep reasoning across the whole repo)| Tool | Install |
|---|---|
| Claude Code | pip install arbiterx-gate then /plugin install NeelPrime/arbiterx |
| Codex CLI | pip install arbiterx-gate then codex plugin install NeelPrime/arbiterx |
Why two steps? The plugin injects engineering rules. The pip package provides the quality gate that actively scores and rejects bad code.
Don't want to install anything? Add the marketplace directly in Claude Code:
/plugin marketplace add NeelPrime/arbiterx
/plugin install arbiterx@arbiterx-marketplaceThis gives you the 10 engineering rules in your prompt — no quality gate scoring, but your AI will still follow the discipline.
| Tool | File to Copy |
|---|---|
| Cursor | .cursor/rules/arbiterx.mdc |
| Windsurf | .windsurf/rules/arbiterx.md |
| GitHub Copilot | .github/copilot-instructions.md |
| Aider | AGENTS.md |
| Kiro | .kiro/steering/arbiterx.md |
| Zed | .zed/assistant/rules.md |
| Any tool | AGENTS.md (universal) |
pip install arbiterx-gate # required
cp hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
# Now rejects commits scoring below 70/100Tested on 10 real queries against its own codebase (46 files, 1364 symbols):
| Query | Naive (full files) | ArbiterX (map) | Saved |
|---|---|---|---|
| "How does TaskClassifier work?" | 9,938 | 600 | 94% |
| "Fix bug in file hasher" | 35,581 | 1,062 | 97% |
| "Add new adapter for Mistral" | 14,263 | 643 | 95% |
| "How is PageRank used?" | 45,300 | 1,034 | 97% |
| Total (10 queries) | 196,247 | 5,639 | 97.1% |
| Language | Status | Language | Status | Language | Status |
|---|---|---|---|---|---|
| Python | ✅ | TypeScript | ✅ | JavaScript | ✅ |
| Go | ✅ | Rust | ✅ | Java | ✅ |
| C / C++ | ✅ | C# | ✅ | Ruby | ✅ |
| PHP | ✅ | Swift | ✅ | Kotlin | ✅ |
| Scala | ✅ | Elixir | ✅ | Haskell | ✅ |
| OCaml | ✅ | Lua | ✅ | Zig | ✅ |
| Bash | ✅ | JSX | ✅ | TSX | ✅ |
| Provider | Models | Local? |
|---|---|---|
| Anthropic | Claude Opus, Sonnet, Haiku | No |
| OpenAI | GPT-4o, o1, o3 | No |
| Gemini 2.0 Pro/Flash | No | |
| Ollama | Any model (Qwen, Llama, Mistral) | Yes |
| OpenRouter | All models (unified) | No |
Does it slow down my workflow?
No. Map builds in <1 second. Queries take 0.5ms. The quality gate runs regex — no LLM calls.
Does it work offline?
Yes. The map, router, and quality gate all run locally. Only the LLM adapters need internet (use Ollama for fully offline).
What if my code fails the gate?
You get a score and specific issues with suggested fixes. Fix them or lower the threshold in config.
Is it free?
Yes. Apache-2.0 license. Zero telemetry. Your code never leaves your machine.
How is this different from a linter?
Linters check syntax. ArbiterX enforces engineering decisions — YAGNI, proper abstractions, security patterns, and architectural discipline. It also handles context compression and model routing, which linters don't touch.
- Saves money — 97% fewer tokens = 97% lower API bills
- Saves time — No more reviewing AI slop in PRs
- Works everywhere — Claude Code, Codex, Cursor, Copilot, Aider + 5 more
- Zero config —
pip install arbiterx-gate && arbiterx initand you're done - Runs locally — Your code never leaves your machine
- Actually tested — 109 tests, benchmarked on real code, not vaporware
pip install arbiterx-gate && arbiterx init && arbiterx mapIf it saves you from one bad AI-generated commit, ⭐ give it a star.
| Component | Technology | Why |
|---|---|---|
| AST Parsing | tree-sitter | Fast, accurate, 22 grammars |
| Symbol Ranking | PageRank (NetworkX) | Surfaces what matters |
| Storage | SQLite | Zero-config, fast, portable |
| CLI | Typer + Rich | Beautiful terminal output |
| HTTP | httpx (async) | Modern, streaming-capable |
| Config | TOML | Human-readable |
Apache-2.0 — Use it anywhere. No strings attached.
Stop reviewing AI slop. Let ArbiterX be the gatekeeper.