Skip to main content

OpenPullRequest

logo

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.

CI License


Built primarily for local-first use — pair it with Ollama and a Gemma model (Google's Gemma family is currently the best fit for this kind of task at a size you can run locally) 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+
  • git on 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: and REVIEW 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: and REVIEW PROMPT: headers to override default system prompts.
  • Updated Config class to load and expose custom prompt overrides.
  • Updated run_init and run_review to prioritize custom prompts over built-in defaults.
  • Enhanced config_describe to inform users of active prompt overrides.
  • Updated README.md with new installation instructions and documentation for custom prompts.

🧪 How Has This Been Tested?

  • Extensive unit tests in tests/test_config.py covering delimiter splitting, prompt parsing, and configuration preservation.
  • Functional tests in tests/test_pr.py verifying 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

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

---
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.

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

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

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>.

Download files

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

Source Distribution

openpurr-0.8.6.tar.gz (539.4 kB view details)

Uploaded Source

Built Distribution

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

openpurr-0.8.6-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file openpurr-0.8.6.tar.gz.

File metadata

  • Download URL: openpurr-0.8.6.tar.gz
  • Upload date:
  • Size: 539.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for openpurr-0.8.6.tar.gz
Algorithm Hash digest
SHA256 cd834ac17cce1b81538c1f2679bef9f087ec3c8c61264e64d88dd1043ae4bc2e
MD5 14cc9fdee963b58ab012edbb61c286c4
BLAKE2b-256 a4c48eb80694acbbb201b4145b35f35300001eea563ba99bd0c537da7ed86613

See more details on using hashes here.

Provenance

The following attestation bundles were made for openpurr-0.8.6.tar.gz:

Publisher: publish.yml on ilypopv/openpurr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openpurr-0.8.6-py3-none-any.whl.

File metadata

  • Download URL: openpurr-0.8.6-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for openpurr-0.8.6-py3-none-any.whl
Algorithm Hash digest
SHA256 0a633e07b14619b0288fdf632e2fa66ff1cd849c0d75cf6527c1b6057abd5960
MD5 57ed78177d136f309864edde5fbea937
BLAKE2b-256 59096b4eaac73d366ff0840f56d3e896a5aa3aeb2eda9965489758533015858a

See more details on using hashes here.

Provenance

The following attestation bundles were made for openpurr-0.8.6-py3-none-any.whl:

Publisher: publish.yml on ilypopv/openpurr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.8.9

2 files

0.8.8

2 files

0.8.7

2 files

This release

0.8.6 This release

2 files

0.8.5

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page