Skip to main content

Automated multi-agent code review tool for GitHub PRs

Project description

MARX - Multi-Agentic Review eXperience

Build status MIT License Python

An interactive CLI tool for automated multi-model AI code review of GitHub Pull Requests. Marx fetches open PRs and runs parallel code reviews inside Docker using three AI models (Claude, Codex, and Gemini) without modifying your local repository.

TL;DR: Install, Configure, Run

# 1) Install (no checkout needed)
uv tool install marx-ai

# 2) Configure credentials
# Create a GitHub token at https://github.com/settings/tokens with 'repo' scope
cat > ~/.marx <<'EOF'
GITHUB_TOKEN=ghp_your_token_here
ANTHROPIC_API_KEY=your_claude_key
OPENAI_API_KEY=your_openai_key
GEMINI_API_KEY=your_gemini_key
# Optional: override repo detection
# MARX_REPO=owner/repo
EOF

# 3) Run on a repo with all agents
cd /path/to/your/git/repo
marx --pr 123 --agents claude,codex,gemini

Features

  • Multi-Model AI Review: Runs Claude, Codex, and Gemini in parallel for comprehensive code analysis
  • Containerized Checkout: Clones the PR branch inside Docker so your local repo stays untouched
  • Intelligent PR Filtering: Automatically filters PRs where you're not the author or reviewer
  • Docker Isolation: All AI models run in containers with proper permissions
  • Structured Review Files: Line-based, machine-parseable review outputs from each agent
  • Robust Error Handling: Graceful fallbacks and comprehensive validation
  • Collision-free Containers: Each agent run uses a unique Docker container name to avoid clashes
  • Preflight PR Context: Captures PR details, diff, changed files, and review comments up front to keep agents aligned on the exact PR state
  • User-Friendly Interface: Colored output with clear progress indicators

Prerequisites

The following tools must be installed:

  • git - Version control
  • gh - GitHub CLI (must be authenticated)
  • docker - Container runtime

Installation

Option 1: Install the published package from PyPI

If you simply want to use Marx and do not need the repository checked out locally, install the published package directly from PyPI using uv:

# Install globally
uv tool install marx-ai

# Or run without installing
uvx marx-ai

After installation the marx CLI will be on your PATH and you can run marx --help to verify everything is set up correctly.

Option 2: Using Nix (Recommended for development)

If you have Nix with flakes enabled:

# Clone the repository
git clone https://github.com/forketyfork/marx.git
cd marx

# Install globally to your profile
nix profile install .

# Or run without installing
nix run .

# Or enter development environment
nix develop

Option 3: From source with uv

For standard Python environments when working from a clone of the repository:

# Clone the repository
git clone https://github.com/forketyfork/marx.git
cd marx

# Install globally
uv pip install .

# Or install in editable mode for development
uv pip install -e ".[dev]"

Note: If you're using Nix, uv pip install will fail because Nix's Python environment is read-only. Use the Nix installation methods above instead.

Running Marx

After installation, run:

marx

Structured JSON output

Use --json-output to print the merged review in a machine-readable format instead of the default rich terminal view:

marx --pr 123 --json-output

The output includes PR context, issue counts, all issues, and pointers to the saved artifacts:

{
  "pr": {"number": 123, "title": "Add new feature"},
  "descriptions": [{"agent": "claude", "description": "Short summary"}],
  "counts": {"total": 5, "p0": 1, "p1": 3, "p2": 1},
  "issues": [
    {
      "agent": "claude",
      "priority": "P1",
      "file": "src/app.py",
      "line": 42,
      "commit_id": "abcd1234",
      "category": "bug",
      "description": "Potential issue",
      "proposed_fix": "Suggested change"
    }
  ],
  "artifacts": {
    "claude_review": "runs/pr-123-main/claude-review.txt",
    "codex_review": "runs/pr-123-main/codex-review.txt",
    "gemini_review": "runs/pr-123-main/gemini-review.txt",
    "dedup_review": null,
    "merged_review": "runs/pr-123-main/merged-review.txt"
  },
  "run_directory": "runs/pr-123-main"
}

Authentication notes

Marx uses the GitHub CLI (gh) under the hood. If you haven't run gh auth login, Marx will try to authenticate gh by supplying a token from your environment/config:

  • If GH_TOKEN is already set, it will be used as-is
  • Else, Marx will look for MARX_GITHUB_TOKEN or GITHUB_TOKEN (env or ~/.marx) and pass it to gh as GH_TOKEN

Recommended options:

  • Either run gh auth login once and rely on your gh keychain, or
  • Put GITHUB_TOKEN=... (or MARX_GITHUB_TOKEN=...) into ~/.marx as shown above, or export it in your shell environment.
  • Use a classic personal access token with repo (or public_repo for public-only) and read:org scopes. Fine-grained tokens are not supported.

If you see an error like:

Repository 'owner/repo' not found or not accessible

ensure that your token has at least the repo scope and that the repository exists and your account has access to it. For GitHub Enterprise, also verify your gh host configuration.

Quick Start with Nix (Recommended for Development)

If you have Nix with flakes enabled:

# Clone the repository
git clone https://github.com/forketyfork/marx.git
cd marx

# Enter the development environment
nix develop

# Or use direnv for automatic environment loading
direnv allow

The Nix flake provides:

  • Python 3.12 with all dependencies
  • System tools (git, gh, docker)
  • Development tools (pytest, black, ruff, mypy)
  • Just command runner for common tasks

Using Just Commands

# See all available commands
just

# Run linters
just lint

# Run tests
just test

# Run marx
just run

# Cut a release (updates versions, commits, tags, pushes)
just release v0.1.1   # creates and pushes branch release-0.1.1 and tag v0.1.1

# Install package in editable mode
just install

# Run all checks (CI equivalent)
just check

Environment Variables

Setting up Environment Variables

Copy the example environment file and fill in your credentials:

cp .env.example .env
# Edit .env with your API keys and tokens

If using direnv (recommended), the .env file will be automatically loaded.

Required

  • GITHUB_TOKEN - GitHub API token (required for container access to GitHub API)
    • Create at https://github.com/settings/tokens
    • Use a classic PAT (fine-grained tokens are not supported)
    • Required scopes: repo (full control of private repositories) and read:org
    • For public repositories only, public_repo + read:org is sufficient
    • The token is used to fetch PR metadata, read diffs, and post reviews

API Keys (at least one required)

The following API keys enable the respective AI models to function. You can provide one or more:

  • ANTHROPIC_API_KEY - Anthropic API key for Claude
  • OPENAI_API_KEY - OpenAI API key for Codex
  • GOOGLE_API_KEY or GEMINI_API_KEY - Google API key for Gemini

Without API keys, the AI models will fall back to using local configuration from ~/.claude, ~/.codex, or ~/.gemini directories if available.

Optional

  • MARX_REPO - Optional owner/name override (e.g., owner/repo) when auto-detection fails

Configuration

Marx supports two authentication methods for AI models:

Method 1: API Keys (Recommended for CI/CD)

Set environment variables with your API keys:

export ANTHROPIC_API_KEY="your-key-here"
export OPENAI_API_KEY="your-key-here"
export GOOGLE_API_KEY="your-key-here"

Method 2: Local Configuration Directories

Marx can use AI model configuration directories from your home directory:

  • ~/.claude - Claude CLI configuration
  • ~/.codex - Codex CLI configuration
  • ~/.gemini - Gemini CLI configuration

These directories are mounted read-only into the Docker container during execution. This method is useful for development when you've already authenticated via the respective CLI tools.

Global Config File (~/.marx)

Marx also looks for a configuration file at ~/.marx. Any values defined here act as defaults and are only applied when the corresponding environment variable is unset. The file uses a simple KEY=value format (an optional leading export is allowed, e.g., export GITHUB_TOKEN=...) and supports comments starting with # on their own line.

Example:

# Default repository for GitHub operations
MARX_REPO=acmecorp/my-app

# API keys (omit or override with environment variables as needed)
GITHUB_TOKEN=ghp_example
OPENAI_API_KEY="sk-123"

This file is loaded automatically when the CLI starts. Environment variables still take precedence, allowing you to override individual values for a single run.

Customizing the review prompt

The default review instructions provided to each agent are stored in the package at marx/prompts/review_prompt.md. You can supply your own prompt template by either setting the MARX_REVIEW_PROMPT_PATH environment variable or adding REVIEW_PROMPT_PATH=/path/to/prompt.md to your ~/.marx configuration file. The template supports the placeholders {pr_number}, {repo}, {commit_sha}, {agent}, and {container_workspace_dir}, which are replaced at runtime.

Deduplication prompt

When more than one agent is selected, Marx automatically invokes an agent for a follow-up deduplication pass. By default, the first agent in the --agents list is used, but you can override this with the --dedupe-with option. This step reads the individual review outputs, asks the agent to merge duplicate issues, and writes the consolidated results to runs/<pr>/dedup-review.txt, ensuring the agent field lists all reporting agents as a comma-separated value. You can customize the deduplication instructions by providing a template at MARX_DEDUP_PROMPT_PATH or DEDUP_PROMPT_PATH (in ~/.marx). The bundled template lives at marx/prompts/dedup_prompt.md. The Docker runner copies this file from .marx/dedup-review.txt in the workspace back to the run directory, so keep custom output names aligned with that location. The template is rendered with Python string formatting, so escape any literal braces as {{ and }} when editing to avoid malformed prompts.

Usage

marx [OPTIONS]

Use marx --help for a quick reminder of prerequisites, environment variables, and example commands.

Options

  • --help - Show help message and exit
  • --version - Show version and exit
  • --pr <number> - Specify PR number directly (skip interactive selection)
  • --agents <agents> - Comma- or space-separated list of agents to run (claude, codex, gemini). Append :<model> to override the model used by that agent.
    • Default: all agents
    • Claude accepts aliases like opus, sonnet, and haiku, or full release identifiers such as claude-sonnet-4-5-20250929. See the Claude models documentation for available models.
    • Codex works with OpenAI models including gpt-5 and gpt-5-codex, plus any custom providers configured in ~/.codex/config.toml.
    • Gemini supports model IDs such as gemini-2.5-flash-lite, gemini-2.5-flash, and gemini-2.5-pro. See the Gemini models documentation for the complete list of available models.
  • --repo <owner/repo> - Repository in the format owner/repo (e.g., acmecorp/my-app)
    • Overrides automatic repository detection
  • --resume - Reuse artifacts from the previous run and skip AI execution
  • --dedupe-with <agent> - Agent to use for deduplication (claude, codex, gemini). Append :<model> to override the model.
    • Default: first agent from --agents

Examples

# Interactive mode with all agents (default)
marx

# Review PR #123 with all agents
marx --pr 123

# Review PR #123 with Claude only
marx --pr 123 --agents claude

# Interactive mode with Codex and Gemini
marx --agents codex,gemini

# Run Claude Opus with Codex defaults
marx --agents "claude:opus,codex"

# Review with Gemini 2.5 Pro only
marx --agents "gemini:gemini-2.5-pro"

# Review PRs in specific repository
marx --repo acmecorp/my-app

# Review specific PR in specific repository
marx --pr 123 --repo acmecorp/my-app

# Review specific PR with custom Claude + Gemini models
marx --pr 456 --agents "claude:sonnet,gemini:gemini-1.5-pro"

# Resume from previous run without rerunning agents
marx --resume --pr 123

# Use Claude Opus for deduplication while running all agents
marx --pr 123 --dedupe-with claude:opus

# Run Codex and Gemini, use Claude for deduplication
marx --agents codex,gemini --dedupe-with claude

How It Works

1. Setup & Validation

  • Checks for required dependencies (git, gh, docker)
  • Pulls the configured Docker image (default: ghcr.io/forketyfork/marx:latest) if not present
  • Validates GITHUB_TOKEN environment variable
  • Confirms current directory is a git repository

2. Repository Detection

Determines repository slug (owner/name) using three methods in order:

  1. MARX_REPO environment variable
  2. gh repo view command
  3. Git remote URL parsing (fallback)

3. PR Discovery

  • Gets current GitHub user via GitHub API
  • Fetches open PRs with metadata (title, author, reviewers, line changes)
  • Filters PRs where:
    • You are NOT the author
    • You are NOT a reviewer
    • Has at least one reviewer assigned
  • Handles both flat array and nested nodes[] API response formats

4. PR Selection & Checkout

  • If --pr is specified: validates PR exists and gets branch name
  • Otherwise: displays formatted PR list with colors and statistics and prompts for selection
  • Fetches the PR and gets commit SHA
  • Clones the repository inside the Docker container and checks out the PR head (or specific commit SHA)

5. Parallel AI Code Review

  • If --agents is specified: runs only the selected agents
  • Otherwise: runs all three agents (claude, codex, gemini)

Each AI model receives a detailed prompt instructing it to:

  • Gather PR context using gh commands
  • Review code for bugs, security issues, performance problems, etc.
  • Output findings in a structured text format

Selected models run simultaneously in isolated Docker containers with:

  • Repository clone inside the container
  • Run artifacts directory mounted from the host
  • Config directories from home
  • GitHub token for API access

6. Results Merging & Display

  • Validates all structured review outputs
  • Merges reviews from all three models
  • Sorts issues by priority (P0 → P1 → P2)
  • Displays formatted output with:
    • PR summary and title
    • Description from each AI model
    • Issue counts by priority
    • Detailed issue breakdown

Output Format

Review Text Structure

Each AI model produces a structured text file with this format:

PR_NUMBER: 123
PR_TITLE: PR Title
PR_DESCRIPTION:
  Brief description of changes

--- ISSUE ---
agent: claude|codex|gemini
priority: P0|P1|P2
path: path/to/file.js
line: 42
commit_id: abcd1234
category: bug|security|performance|quality|style
description:
  Detailed description of the issue
proposed_fix:
  Concrete suggestion on how to fix it

Priority Definitions

  • P0 (Critical): Must be fixed - security vulnerabilities, bugs causing crashes/data loss
  • P1 (Important): Should be fixed - logic bugs, performance problems, poor error handling
  • P2 (Nice-to-have): Suggestions - code style, minor optimizations

Output Files

All files are saved in the run artifacts directory (runs/pr-{number}-{branch}/):

  • claude-review.txt - Claude's review
  • codex-review.txt - Codex's review
  • gemini-review.txt - Gemini's review
  • dedup-review.txt - Deduplicated issues when multiple agents run
  • merged-review.txt - Combined review from all models

Example Workflow

# Run marx
marx

# The tool will:
# 1. Detect your repository
# 2. Show available PRs
# 3. Prompt you to select one
# 4. Clone the PR inside Docker and run AI reviews in parallel
# 5. Display merged results and write artifacts to runs/pr-<number>-<branch>/

# If you want a local checkout to apply fixes:
gh pr checkout 123

# Review artifacts are available at:
ls runs/pr-123-<branch>/

Docker Image

Marx uses a Docker image containing:

  • AI CLI Tools: Claude, Codex, Gemini
  • GitHub Tools: gh (GitHub CLI)
  • Search & Navigation: rg (ripgrep), fd, tree
  • Code Refactoring: fastmod, ast-grep (with sg alias)
  • Development Tools: git and other utilities

The default image is ghcr.io/forketyfork/marx:latest. It is published to GHCR and pulled automatically on first run.

Customizing the Docker image

You can run Marx with a different Docker image by setting the MARX_DOCKER_IMAGE environment variable or by adding DOCKER_IMAGE=your/image:tag to your ~/.marx configuration file. The environment variable takes precedence over the config file, and both fall back to the bundled ghcr.io/forketyfork/marx:latest image when unspecified.

When supplying a custom image make sure it satisfies the baseline requirements expected by the runner script:

  • A Linux base with /bin/bash, core utilities, and the ability to create users via useradd
  • git and gh (GitHub CLI)
  • Search and navigation tools: rg, fd, tree
  • Code editing helpers: fastmod, ast-grep (available as sg)
  • The CLI tools for any agents you intend to run (claude, codex, gemini)

Images missing these tools will fail at runtime, so verify your custom build before invoking Marx.

If you prefer a local build, build the image yourself (for example, docker build -t marx:latest .) and set MARX_DOCKER_IMAGE=marx:latest so Marx uses your local tag.

Error Handling

Marx includes robust error handling:

  • Non-compliant review outputs from AI models are handled gracefully with empty reviews
  • Failed API calls are caught and reported with detailed error messages
  • Docker errors and container stderr are both captured and displayed
  • Invalid review formats are replaced with valid fallback structures
  • All temporary files are cleaned up automatically
  • Helpful hints are provided when authentication or configuration issues occur

Security Considerations

  • AI models run in isolated Docker containers
  • Config directories are mounted read-only
  • GitHub token is passed as an environment variable
  • No destructive git operations are performed
  • User input is validated before use

Troubleshooting

"GITHUB_TOKEN environment variable is not set"

Set your GitHub token: export GITHUB_TOKEN=ghp_your_token_here

"Unable to determine repository automatically"

Set the repository manually: export MARX_REPO=owner/repo

"Missing required dependencies"

Install the missing tools listed in the error message.

AI model fails or returns invalid review output

Marx will automatically handle this by creating an empty review. Error details are displayed in the terminal output, including:

  • Docker errors (if Docker command failed)
  • Container stderr output (errors from the AI CLI tool)
  • Helpful hints for common issues (missing/invalid credentials)

Common causes:

  • Missing authentication: Set API key environment variables (e.g., ANTHROPIC_API_KEY) or run the CLI auth command (e.g., claude auth for Claude)
  • Invalid API keys or credentials in ~/.claude/, ~/.codex/, or ~/.gemini/
  • Network connectivity issues
  • API quota/rate limiting

Tip: For CI/CD environments, using API key environment variables is more reliable than local configuration directories.

Development & Testing

Nix Development Workflow (Recommended)

The project includes a Nix flake for reproducible development environments:

# Enter development shell
nix develop

# Or use direnv for automatic loading (recommended)
echo "use flake" > .envrc
direnv allow

# Use just for common tasks
just             # List all commands
just check       # Run all checks (lint + type-check + test)
just lint        # Run linters
just test        # Run tests
just run --pr 123  # Run marx

Setting up direnv

  1. Install direnv: nix-env -iA nixpkgs.direnv or see direnv installation
  2. Add hook to your shell (e.g., eval "$(direnv hook bash)" for bash)
  3. Allow the directory: direnv allow

Now the environment will automatically load when you cd into the project!

Just Command Reference

just install       # Install package in editable mode
just lint          # Run all linters (black, ruff, mypy)
just format        # Format code with black
just fix           # Auto-fix linting issues
just test          # Run all tests
just test-cov      # Run tests with coverage
just test-file FILE  # Run specific test file
just clean         # Clean build artifacts
just docker-build  # Build Docker image
just info          # Show environment info

Python Version

The Python codebase includes a comprehensive test suite. To run tests manually:

# Install development dependencies (includes pytest-cov for coverage)
uv pip install -e ".[dev]"
# or
pip install -r requirements-dev.txt

# Run tests with coverage
pytest

# Run tests with verbose output
pytest -v

# Run specific test file
pytest tests/test_github.py

# Type checking with mypy
mypy marx

# Code formatting with black
black marx tests

# Linting with ruff
ruff check marx tests

Project Structure

marx/
├── marx/          # Main package
│   ├── __init__.py
│   ├── cli.py          # CLI entry point and orchestration
│   ├── config.py       # Configuration and constants
│   ├── docker_runner.py # Docker container orchestration
│   ├── exceptions.py   # Custom exceptions
│   ├── github.py       # GitHub API client
│   ├── review.py       # Review processing and merging
│   └── ui.py           # Terminal UI and formatting
├── tests/              # Test suite
│   ├── conftest.py     # Pytest fixtures
│   ├── test_github.py  # GitHub client tests
│   └── test_review.py  # Review processing tests
├── pyproject.toml      # Project configuration
├── requirements.txt    # Dependencies
└── README.md           # This file

Publishing

Marx ships with an automated publication pipeline defined in .github/workflows/publish.yml. The workflow builds source and wheel distributions and uploads them as a workflow artifact on every run. Because uploading artifacts requires elevated GitHub token capabilities, the workflow explicitly grants the actions: write permission at the workflow level. When a GitHub release is published, the same workflow also pushes the distributions to PyPI using the PYPI_API_TOKEN repository secret.

Preparing a release

  1. Update the version number in pyproject.toml.
  2. Run just build locally to confirm the package can be built.
  3. Commit and push the release changes, then create a Git tag (for example v1.2.3).
  4. Draft a GitHub release for the tag and click Publish to trigger the PyPI deployment.

Configuring credentials

  • Create a PyPI API token.
  • Add the token to the repository secrets as PYPI_API_TOKEN (set the username to __token__).
  • Ensure the token has permission to upload to the marx-ai project on PyPI.

Manual runs of the workflow via Run workflow in the Actions tab will build and attach the distributions without publishing them, making it easy to validate the packaging process before performing a release.

Contributing

Contributions are welcome! Please ensure:

  • Code passes mypy type checking
  • Code passes ruff linting
  • Code is formatted with black
  • Tests pass with pytest
  • New features include tests
  • Follow Python best practices and PEP 8

License

See LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

marx_ai-0.3.0.tar.gz (48.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

marx_ai-0.3.0-py3-none-any.whl (38.0 kB view details)

Uploaded Python 3

File details

Details for the file marx_ai-0.3.0.tar.gz.

File metadata

  • Download URL: marx_ai-0.3.0.tar.gz
  • Upload date:
  • Size: 48.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for marx_ai-0.3.0.tar.gz
Algorithm Hash digest
SHA256 a870f5b33319aad2ff35af4080cab6402a8b687e8756ce069dcce3a011b463a9
MD5 a5aa1a0cf2599d3c4c4c10f09b4b264d
BLAKE2b-256 fbdec4025802270cb610b080c296c7e5fec2a9efe0862494b3cc02ee446c65ac

See more details on using hashes here.

File details

Details for the file marx_ai-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: marx_ai-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 38.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for marx_ai-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c0397c29d7efabc2893a1c43538febbd0fae188804145868b88b0661dbe5065f
MD5 4f9327abe516c23fe9662a827fdc4dc3
BLAKE2b-256 04425ce238f40ad019e00b820ced0ab691bc44d27e6554330076b3bceebaa813

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page