Metadata-Version: 2.5
Name: openpurr
Version: 0.8.5
Summary: CLI tool to generate PR titles, descriptions, and post-review change summaries using a local or cloud LLM.
Requires-Python: >=3.11
Requires-Dist: anthropic>=0.122.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: openai>=1.0.0
Requires-Dist: questionary>=2.0.0
Requires-Dist: rich>=13.7.0
Requires-Dist: typer>=0.12.0
Description-Content-Type: text/markdown

# OpenPullRequest

<img src="https://raw.githubusercontent.com/ilypopv/openpurr/main/imgs/openpurr.png" alt="logo" width="180" align="left">

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](https://img.shields.io/github/actions/workflow/status/ilypopv/openpurr/ci.yml?style=flat-square&label=CI)](https://github.com/ilypopv/openpurr/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)

<br clear="left" />

Built primarily for local-first use — pair it with [Ollama](https://ollama.com) 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**

```bash
uv tool install openpurr
```

Or

```bash
pipx install openpurr
```

On first run, the setup wizard creates `~/.openpurr` automatically. You can also run it any time:

```bash
opo setup
```

## Requirements

- Python 3.11+
- `git` on PATH
- One of: [Ollama](https://ollama.com) running locally, or an API key for a cloud provider

## Usage

```bash
# 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
```

## Configuration

Settings are stored in `~/.openpurr` as flat `OPO_KEY=value` lines — no sections, no quoting:

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

```bash
# 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.

```text
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)

```bash
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

```bash
opo config set keep_alive 0s
```

## Models

List available models for the current or a specified provider:

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