OpenPullRequest
CLI tool that generates PR titles, descriptions, and post-review change summaries using a local or cloud LLM. Supports Ollama, OpenAI, Anthropic, Google Gemini, OpenRouter, DeepSeek, llama.cpp, and MLX.
Built primarily for local-first use — pair it with Ollama and Gemma or Ornith or any other model you like for a free, fully offline workflow. On Apple Silicon Macs, prefer the mlx tagged variant of your chosen model (e.g. gemma4:26b-mlx) — it runs on Apple's MLX framework instead of plain GGUF and is noticeably faster on that hardware. Don't want to run anything locally? Google's Gemini API has a generous free tier and is a great low-friction cloud option.
Installation
Recommended
uv tool install openpurr
Or
pipx install openpurr
On first run, the setup wizard creates ~/.openpurr automatically. You can also run it any time:
opo setup
Requirements
- Python 3.11+
giton PATH- One of: Ollama running locally, or an API key for a cloud provider
Usage
# Generate a PR title + description from the diff against main
opo
# Diff against a different base branch
opo --base develop
# Summarise changes made since the last N commits (for reviewers)
opo review
opo review --commits 3
Example output
Running opo against a diff produces a title and description like this — generated
with the default system prompt, no custom overrides. This is PR #11 from this very repo — in fact, every PR in openpurr's history was generated by openpurr itself:
feat(config): add support for custom system prompt overrides
📝 Summary
Introduced a mechanism to override default system prompts via the configuration file. Users can now define custom
INIT PROMPT:andREVIEW PROMPT:sections to tailor LLM behavior for PR generation and reviews.🛠 Type of Change
- New feature
🔍 Key Changes
- Implemented a new configuration parsing logic that uses a
---delimiter to separate key-value pairs from free-text prompt sections.- Added support for
INIT PROMPT:andREVIEW PROMPT:headers to override default system prompts.- Updated
Configclass to load and expose custom prompt overrides.- Updated
run_initandrun_reviewto prioritize custom prompts over built-in defaults.- Enhanced
config_describeto inform users of active prompt overrides.- Updated
README.mdwith new installation instructions and documentation for custom prompts.🧪 How Has This Been Tested?
- Extensive unit tests in
tests/test_config.pycovering delimiter splitting, prompt parsing, and configuration preservation.- Functional tests in
tests/test_pr.pyverifying that custom prompts are correctly passed to the LLM provider.
Want a different tone or format? See Custom system prompts below.
Configuration
Settings are stored in ~/.openpurr as flat OPO_KEY=value lines — no sections, no quoting:
OPO_PROVIDER=ollama
OPO_MODEL=gemma4:26b
OPO_API_KEY=
OPO_HOST=http://localhost:11434
OPO_TEMPERATURE=0.0
OPO_KEEP_ALIVE=5m
OPO_BASE=main
OPO_LANGUAGE=en
Use the config subcommand to inspect and update values without editing the file directly.
# Show all keys with descriptions and current values
opo config describe
# Read a single value
opo config get model
# Update a value
opo config set provider ollama
opo config set model gemma4:26b
opo config set api_key sk-...
opo config set keep_alive 0s
Custom system prompts
The prompts opo sends to the LLM aren't fixed — everything after a lone --- line in
~/.openpurr is free text, not OPO_KEY=value config, so it can hold Markdown, headers,
= signs, anything. Add an INIT PROMPT: section to override the prompt behind opo
(title + description) and/or a REVIEW PROMPT: section to override the one behind
opo review. Either section is optional; whichever is missing keeps the built-in default.
OPO_PROVIDER=ollama
OPO_MODEL=gemma4:26b
OPO_API_KEY=
OPO_HOST=http://localhost:11434
OPO_TEMPERATURE=0.0
OPO_KEEP_ALIVE=5m
OPO_BASE=main
OPO_LANGUAGE=en
---
INIT PROMPT:
You are a Senior Principal Engineer. Write a terse PR title and description in
Conventional Commits style. No emoji, no checklists.
REVIEW PROMPT:
Summarize what changed since the last review in one short paragraph.
opo config set/opo config describe only ever touch the OPO_KEY=value part above the
--- line — the prompt section is left exactly as you wrote it, and opo config describe
tells you which of init/review are currently overridden. Note that OPO_LANGUAGE (see
below) only affects the built-in prompts — once you write your own INIT PROMPT:/REVIEW PROMPT:,
you're in full control, so specify the language directly in the prompt text if you need one.
Configuration keys
| Key | Env var | Default | Description |
|---|---|---|---|
provider |
OPO_PROVIDER |
ollama |
ollama · openai · anthropic · gemini · openrouter · deepseek · llamacpp · mlx |
model |
OPO_MODEL |
(empty) | Model name — set via opo setup or opo config set model <name> |
api_key |
OPO_API_KEY |
(empty) | API key for cloud providers |
host |
OPO_HOST |
http://localhost:11434 |
Base URL — Ollama default; set to a custom endpoint when needed |
temperature |
OPO_TEMPERATURE |
0.0 |
Sampling temperature (0.0 = deterministic) |
keep_alive |
OPO_KEEP_ALIVE |
5m |
Ollama VRAM keep-alive (0s = unload immediately, 5m = keep warm) |
base |
OPO_BASE |
main |
Default base branch to diff against |
language |
OPO_LANGUAGE |
en |
Output language for generated PR text (ISO 639-1 code, e.g. es, fr, de, ja, zh) |
There's no hardcoded default model: opo setup always has you pick one (from a live-fetched list, or typed manually), and opo/opo review will error out with a pointer back to opo setup if model is ever left blank (e.g. after hand-editing the file).
Example: switch to Google Gemini (free tier)
opo config set provider gemini
opo config set model <your-chosen-model>
opo config set api_key <your-gemini-api-key>
Model names aren't hardcoded anywhere in this README on purpose — providers retire and rename models constantly. Run opo setup or opo models --provider gemini to pick from what's actually available right now.
Example: unload Ollama from VRAM after each request
opo config set keep_alive 0s
Example: generate PR text in another language
opo config set language es
opo setup also asks for this up front, with arrow-key selection from common languages
plus a custom-code fallback. This only affects the built-in prompts — see
Custom system prompts above.
Models
List available models for the current or a specified provider:
opo models
opo models --provider anthropic
Model lists are always fetched live from the provider — Ollama's /api/tags, OpenAI/Anthropic/Gemini/OpenRouter/DeepSeek/llama.cpp/MLX's models.list() (or public models endpoint) — never a hardcoded/curated list baked into the tool. If the fetch fails (offline, bad key, local server not running) the command prints an empty result instead of erroring; check connectivity/credentials, or pull a model directly with ollama pull <model>.
Metadata
Release files for openpurr 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| openpurr-0.9.0.tar.gz | 551.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openpurr-0.9.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 582.7 kB
Release files / openpurr-0.9.0.tar.gz
| Download URL | openpurr-0.9.0.tar.gz |
|---|---|
| Size | 551.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
51c448a97fdac1266d46ed2804eb38e95cbcbcdf9a10fbb258bce58b52580a84
|
|
BLAKE2b-256 checksum How to use checksums |
a0869c87206d476ad373f2762d5a33dd6087edbc74e7b4daba0d7c80f19b7db7
|
| 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 Sep 5, 2026.
Transparency logRelease files / openpurr-0.9.0-py3-none-any.whl
| Download URL | openpurr-0.9.0-py3-none-any.whl |
|---|---|
| Size | 31.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
91910592d54cd09f0c32592d90c267a4177acc438e036e9cbf472ad2d79104d6
|
|
BLAKE2b-256 checksum How to use checksums |
6433166d210816289553e2e133bc72dcfd4dd4add5c3d80ce569263725af0ba7
|
| 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 Sep 5, 2026.
Transparency log