Read this entire file before making any changes to the codebase.
Ebert is an "uncompromising" AI code review CLI. The name references film critic Roger Ebert - the tool provides direct, honest feedback without unnecessary praise.
- Provider Agnostic - All LLM logic goes through the provider abstraction
- Immutability - Domain models are frozen dataclasses
- Composition - No deep inheritance hierarchies
- Minimal Dependencies - Provider SDKs are optional extras
- Defensive Parsing - Never trust LLM output format
- Create
src/ebert/providers/<name>.py - Inherit from
ReviewProviderbase class - Implement
review(),name,model,is_available() - Register via
register_provider(name, factory)in module - Add to
ProviderRegistry.load_all()inregistry.py - Add optional dependency to
pyproject.tomlextras
- Create formatter class in
src/ebert/output/formatter.py - Implement
format(result: ReviewResult) -> str - Add to
get_formatter()factory function
- All new code requires tests in
tests/ - Run
make testbefore committing - Run
make lintto check style
- 2-space indentation (configured in ruff)
- 100 character line length
- Type hints on all public functions
- Docstrings for public APIs only
- No emoji in code or comments
- Don't import provider SDKs at module level (they're optional)
- Don't assume LLM response format - use
extract_json()parser - Don't leak file paths in error messages - sanitize them
- Don't add features beyond what's requested
Ebert supports three review modes:
- Staged changes (default) -
ebertreviewsgit diff --cached - Branch comparison -
ebert --branch feature/xcompares against base - File scanning -
ebert src/*.pyreviews files directly without git
File scanning uses extract_files_as_context() which formats files as synthetic diffs.
src/ebert/
__init__.py # Version definition
cli.py # Entry point (don't add business logic here)
models.py # Domain models (keep frozen)
review.py # Orchestration (main flow)
config/
settings.py # Pydantic settings model
loader.py # YAML loading
diff/
extractor.py # Git operations + file scanning
providers/
base.py # Abstract interface
registry.py # Factory pattern
prompt.py # Shared prompt building
parser.py # JSON extraction
anthropic.py # Provider implementations
openai.py
gemini.py
ollama.py
output/
formatter.py # All formatters
| Variable | Required For |
|---|---|
ANTHROPIC_API_KEY |
anthropic provider |
OPENAI_API_KEY |
openai provider |
GEMINI_API_KEY |
gemini provider |
OLLAMA_HOST |
custom ollama endpoint |