openpurr 0.8.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openpurr-0.8.5/.github/workflows/ci.yml +42 -0
- openpurr-0.8.5/.github/workflows/publish.yml +41 -0
- openpurr-0.8.5/.gitignore +142 -0
- openpurr-0.8.5/PKG-INFO +166 -0
- openpurr-0.8.5/README.md +153 -0
- openpurr-0.8.5/imgs/openpurr.png +0 -0
- openpurr-0.8.5/imgs/openpurr.pxd +0 -0
- openpurr-0.8.5/pyproject.toml +30 -0
- openpurr-0.8.5/src/openpurr/__init__.py +3 -0
- openpurr-0.8.5/src/openpurr/config.py +219 -0
- openpurr-0.8.5/src/openpurr/llm.py +38 -0
- openpurr-0.8.5/src/openpurr/main.py +202 -0
- openpurr-0.8.5/src/openpurr/model_catalog.py +97 -0
- openpurr-0.8.5/src/openpurr/pr.py +119 -0
- openpurr-0.8.5/src/openpurr/providers/__init__.py +0 -0
- openpurr-0.8.5/src/openpurr/providers/anthropic.py +64 -0
- openpurr-0.8.5/src/openpurr/providers/base.py +28 -0
- openpurr-0.8.5/src/openpurr/providers/ollama.py +97 -0
- openpurr-0.8.5/src/openpurr/providers/openai.py +107 -0
- openpurr-0.8.5/src/openpurr/setup_wizard.py +221 -0
- openpurr-0.8.5/src/openpurr/utils/__init__.py +0 -0
- openpurr-0.8.5/src/openpurr/utils/git.py +73 -0
- openpurr-0.8.5/tests/__init__.py +0 -0
- openpurr-0.8.5/tests/test_config.py +357 -0
- openpurr-0.8.5/tests/test_git.py +198 -0
- openpurr-0.8.5/tests/test_llm_factory.py +154 -0
- openpurr-0.8.5/tests/test_model_catalog.py +156 -0
- openpurr-0.8.5/tests/test_ollama_provider.py +208 -0
- openpurr-0.8.5/tests/test_openai_provider.py +140 -0
- openpurr-0.8.5/tests/test_pr.py +83 -0
- openpurr-0.8.5/tests/test_setup_wizard.py +202 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "**" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "**" ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Setup Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v5
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync --dev --python ${{ matrix.python-version }}
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: uv run pytest
|
|
36
|
+
|
|
37
|
+
- name: Upload coverage artifact
|
|
38
|
+
if: matrix.python-version == '3.12'
|
|
39
|
+
uses: actions/upload-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: coverage-xml
|
|
42
|
+
path: coverage.xml
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- name: Install build tools
|
|
18
|
+
run: pip install build twine
|
|
19
|
+
- name: Build package
|
|
20
|
+
run: python -m build
|
|
21
|
+
- name: Check distribution
|
|
22
|
+
run: twine check dist/*
|
|
23
|
+
- name: Store distribution packages
|
|
24
|
+
uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: dist
|
|
27
|
+
path: dist/
|
|
28
|
+
|
|
29
|
+
publish:
|
|
30
|
+
needs: build
|
|
31
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
environment: release
|
|
34
|
+
permissions:
|
|
35
|
+
id-token: write
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/download-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: dist
|
|
40
|
+
path: dist/
|
|
41
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
downloads/
|
|
14
|
+
eggs/
|
|
15
|
+
.eggs/
|
|
16
|
+
lib/
|
|
17
|
+
lib64/
|
|
18
|
+
parts/
|
|
19
|
+
sdist/
|
|
20
|
+
var/
|
|
21
|
+
wheels/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
|
|
26
|
+
# Virtual environments
|
|
27
|
+
.env
|
|
28
|
+
.venv
|
|
29
|
+
venv/
|
|
30
|
+
ENV/
|
|
31
|
+
env/
|
|
32
|
+
|
|
33
|
+
# Jupyter Notebook
|
|
34
|
+
.ipynb_checkpoints/
|
|
35
|
+
*.nbconvert.ipynb
|
|
36
|
+
|
|
37
|
+
# PyCharm
|
|
38
|
+
.idea/
|
|
39
|
+
|
|
40
|
+
# VSCode
|
|
41
|
+
.vscode/
|
|
42
|
+
|
|
43
|
+
# macOS
|
|
44
|
+
.DS_Store
|
|
45
|
+
|
|
46
|
+
# Linux
|
|
47
|
+
*~
|
|
48
|
+
|
|
49
|
+
# Logs
|
|
50
|
+
logs/
|
|
51
|
+
*.log
|
|
52
|
+
|
|
53
|
+
# Testing
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
.coverage
|
|
56
|
+
htmlcov/
|
|
57
|
+
.tox/
|
|
58
|
+
.mypy_cache/
|
|
59
|
+
.ruff_cache/
|
|
60
|
+
|
|
61
|
+
# Type checking
|
|
62
|
+
.pyre/
|
|
63
|
+
|
|
64
|
+
# Data files
|
|
65
|
+
data/
|
|
66
|
+
datasets/
|
|
67
|
+
*.csv
|
|
68
|
+
*.tsv
|
|
69
|
+
*.parquet
|
|
70
|
+
*.feather
|
|
71
|
+
*.h5
|
|
72
|
+
*.hdf5
|
|
73
|
+
*.db
|
|
74
|
+
|
|
75
|
+
# Model artifacts
|
|
76
|
+
models/
|
|
77
|
+
checkpoints/
|
|
78
|
+
*.ckpt
|
|
79
|
+
*.pt
|
|
80
|
+
*.pth
|
|
81
|
+
*.onnx
|
|
82
|
+
*.joblib
|
|
83
|
+
*.pkl
|
|
84
|
+
|
|
85
|
+
# Experiment tracking
|
|
86
|
+
mlruns/
|
|
87
|
+
wandb/
|
|
88
|
+
tensorboard/
|
|
89
|
+
runs/
|
|
90
|
+
|
|
91
|
+
# Outputs
|
|
92
|
+
outputs/
|
|
93
|
+
results/
|
|
94
|
+
figures/
|
|
95
|
+
plots/
|
|
96
|
+
reports/
|
|
97
|
+
|
|
98
|
+
# Temporary files
|
|
99
|
+
tmp/
|
|
100
|
+
temp/
|
|
101
|
+
*.tmp
|
|
102
|
+
|
|
103
|
+
# Secrets / credentials
|
|
104
|
+
.env.local
|
|
105
|
+
.env.*
|
|
106
|
+
!.env.example
|
|
107
|
+
|
|
108
|
+
# Package managers
|
|
109
|
+
Pipfile.lock
|
|
110
|
+
|
|
111
|
+
# Hatch
|
|
112
|
+
.hatch/
|
|
113
|
+
|
|
114
|
+
# DVC
|
|
115
|
+
.dvc/cache/
|
|
116
|
+
.dvc/tmp/
|
|
117
|
+
|
|
118
|
+
# Snakemake
|
|
119
|
+
.snakemake/
|
|
120
|
+
|
|
121
|
+
# R
|
|
122
|
+
.Rhistory
|
|
123
|
+
.RData
|
|
124
|
+
.Rproj.user/
|
|
125
|
+
|
|
126
|
+
# Docker
|
|
127
|
+
*.pid
|
|
128
|
+
|
|
129
|
+
# AWS / cloud
|
|
130
|
+
.aws/
|
|
131
|
+
|
|
132
|
+
# IDE-specific notebooks
|
|
133
|
+
.spyproject/
|
|
134
|
+
|
|
135
|
+
# Ignore large binaries
|
|
136
|
+
*.zip
|
|
137
|
+
*.tar.gz
|
|
138
|
+
*.7z
|
|
139
|
+
uv.lock
|
|
140
|
+
|
|
141
|
+
# Agents markdowns
|
|
142
|
+
CLAUDE.md
|
openpurr-0.8.5/PKG-INFO
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: openpurr
|
|
3
|
+
Version: 0.8.5
|
|
4
|
+
Summary: CLI tool to generate PR titles, descriptions, and post-review change summaries using a local or cloud LLM.
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: anthropic>=0.122.0
|
|
7
|
+
Requires-Dist: httpx>=0.27.0
|
|
8
|
+
Requires-Dist: openai>=1.0.0
|
|
9
|
+
Requires-Dist: questionary>=2.0.0
|
|
10
|
+
Requires-Dist: rich>=13.7.0
|
|
11
|
+
Requires-Dist: typer>=0.12.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# OpenPullRequest
|
|
15
|
+
|
|
16
|
+
<img src="https://raw.githubusercontent.com/ilypopv/openpurr/main/imgs/openpurr.png" alt="logo" width="180" align="left">
|
|
17
|
+
|
|
18
|
+
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.
|
|
19
|
+
|
|
20
|
+
[](https://github.com/ilypopv/openpurr/actions/workflows/ci.yml)
|
|
21
|
+
[](LICENSE)
|
|
22
|
+
|
|
23
|
+
<br clear="left" />
|
|
24
|
+
|
|
25
|
+
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.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
**Recommended**
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv tool install openpurr
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pipx install openpurr
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
On first run, the setup wizard creates `~/.openpurr` automatically. You can also run it any time:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
opo setup
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
- Python 3.11+
|
|
50
|
+
- `git` on PATH
|
|
51
|
+
- One of: [Ollama](https://ollama.com) running locally, or an API key for a cloud provider
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Generate a PR title + description from the diff against main
|
|
57
|
+
opo
|
|
58
|
+
|
|
59
|
+
# Diff against a different base branch
|
|
60
|
+
opo --base develop
|
|
61
|
+
|
|
62
|
+
# Summarise changes made since the last N commits (for reviewers)
|
|
63
|
+
opo review
|
|
64
|
+
opo review --commits 3
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
Settings are stored in `~/.openpurr` as flat `OPO_KEY=value` lines — no sections, no quoting:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
OPO_PROVIDER=ollama
|
|
73
|
+
OPO_MODEL=gemma4:26b
|
|
74
|
+
OPO_API_KEY=
|
|
75
|
+
OPO_HOST=http://localhost:11434
|
|
76
|
+
OPO_TEMPERATURE=0.0
|
|
77
|
+
OPO_KEEP_ALIVE=5m
|
|
78
|
+
OPO_BASE=main
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use the `config` subcommand to inspect and update values without editing the file directly.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Show all keys with descriptions and current values
|
|
85
|
+
opo config describe
|
|
86
|
+
|
|
87
|
+
# Read a single value
|
|
88
|
+
opo config get model
|
|
89
|
+
|
|
90
|
+
# Update a value
|
|
91
|
+
opo config set provider ollama
|
|
92
|
+
opo config set model gemma4:26b
|
|
93
|
+
opo config set api_key sk-...
|
|
94
|
+
opo config set keep_alive 0s
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Custom system prompts
|
|
98
|
+
|
|
99
|
+
The prompts `opo` sends to the LLM aren't fixed — everything after a lone `---` line in
|
|
100
|
+
`~/.openpurr` is free text, not `OPO_KEY=value` config, so it can hold Markdown, headers,
|
|
101
|
+
`=` signs, anything. Add an `INIT PROMPT:` section to override the prompt behind `opo`
|
|
102
|
+
(title + description) and/or a `REVIEW PROMPT:` section to override the one behind
|
|
103
|
+
`opo review`. Either section is optional; whichever is missing keeps the built-in default.
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
OPO_PROVIDER=ollama
|
|
107
|
+
OPO_MODEL=gemma4:26b
|
|
108
|
+
OPO_API_KEY=
|
|
109
|
+
OPO_HOST=http://localhost:11434
|
|
110
|
+
OPO_TEMPERATURE=0.0
|
|
111
|
+
OPO_KEEP_ALIVE=5m
|
|
112
|
+
OPO_BASE=main
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
INIT PROMPT:
|
|
116
|
+
You are a Senior Principal Engineer. Write a terse PR title and description in
|
|
117
|
+
Conventional Commits style. No emoji, no checklists.
|
|
118
|
+
|
|
119
|
+
REVIEW PROMPT:
|
|
120
|
+
Summarize what changed since the last review in one short paragraph.
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`opo config set`/`opo config describe` only ever touch the `OPO_KEY=value` part above the
|
|
124
|
+
`---` line — the prompt section is left exactly as you wrote it, and `opo config describe`
|
|
125
|
+
tells you which of `init`/`review` are currently overridden.
|
|
126
|
+
|
|
127
|
+
### Configuration keys
|
|
128
|
+
|
|
129
|
+
| Key | Env var | Default | Description |
|
|
130
|
+
| --- | ------- | ------- | ----------- |
|
|
131
|
+
| `provider` | `OPO_PROVIDER` | `ollama` | `ollama` · `openai` · `anthropic` · `gemini` · `openrouter` · `deepseek` · `llamacpp` · `mlx` |
|
|
132
|
+
| `model` | `OPO_MODEL` | _(empty)_ | Model name — set via `opo setup` or `opo config set model <name>` |
|
|
133
|
+
| `api_key` | `OPO_API_KEY` | _(empty)_ | API key for cloud providers |
|
|
134
|
+
| `host` | `OPO_HOST` | `http://localhost:11434` | Base URL — Ollama default; set to a custom endpoint when needed |
|
|
135
|
+
| `temperature` | `OPO_TEMPERATURE` | `0.0` | Sampling temperature (`0.0` = deterministic) |
|
|
136
|
+
| `keep_alive` | `OPO_KEEP_ALIVE` | `5m` | Ollama VRAM keep-alive (`0s` = unload immediately, `5m` = keep warm) |
|
|
137
|
+
| `base` | `OPO_BASE` | `main` | Default base branch to diff against |
|
|
138
|
+
|
|
139
|
+
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).
|
|
140
|
+
|
|
141
|
+
### Example: switch to Google Gemini (free tier)
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
opo config set provider gemini
|
|
145
|
+
opo config set model <your-chosen-model>
|
|
146
|
+
opo config set api_key <your-gemini-api-key>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
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.
|
|
150
|
+
|
|
151
|
+
### Example: unload Ollama from VRAM after each request
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
opo config set keep_alive 0s
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Models
|
|
158
|
+
|
|
159
|
+
List available models for the current or a specified provider:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
opo models
|
|
163
|
+
opo models --provider anthropic
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
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>`.
|
openpurr-0.8.5/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# OpenPullRequest
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/ilypopv/openpurr/main/imgs/openpurr.png" alt="logo" width="180" align="left">
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/ilypopv/openpurr/actions/workflows/ci.yml)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
<br clear="left" />
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
**Recommended**
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv tool install openpurr
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pipx install openpurr
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
On first run, the setup wizard creates `~/.openpurr` automatically. You can also run it any time:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
opo setup
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
- Python 3.11+
|
|
37
|
+
- `git` on PATH
|
|
38
|
+
- One of: [Ollama](https://ollama.com) running locally, or an API key for a cloud provider
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Generate a PR title + description from the diff against main
|
|
44
|
+
opo
|
|
45
|
+
|
|
46
|
+
# Diff against a different base branch
|
|
47
|
+
opo --base develop
|
|
48
|
+
|
|
49
|
+
# Summarise changes made since the last N commits (for reviewers)
|
|
50
|
+
opo review
|
|
51
|
+
opo review --commits 3
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
Settings are stored in `~/.openpurr` as flat `OPO_KEY=value` lines — no sections, no quoting:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
OPO_PROVIDER=ollama
|
|
60
|
+
OPO_MODEL=gemma4:26b
|
|
61
|
+
OPO_API_KEY=
|
|
62
|
+
OPO_HOST=http://localhost:11434
|
|
63
|
+
OPO_TEMPERATURE=0.0
|
|
64
|
+
OPO_KEEP_ALIVE=5m
|
|
65
|
+
OPO_BASE=main
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use the `config` subcommand to inspect and update values without editing the file directly.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Show all keys with descriptions and current values
|
|
72
|
+
opo config describe
|
|
73
|
+
|
|
74
|
+
# Read a single value
|
|
75
|
+
opo config get model
|
|
76
|
+
|
|
77
|
+
# Update a value
|
|
78
|
+
opo config set provider ollama
|
|
79
|
+
opo config set model gemma4:26b
|
|
80
|
+
opo config set api_key sk-...
|
|
81
|
+
opo config set keep_alive 0s
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Custom system prompts
|
|
85
|
+
|
|
86
|
+
The prompts `opo` sends to the LLM aren't fixed — everything after a lone `---` line in
|
|
87
|
+
`~/.openpurr` is free text, not `OPO_KEY=value` config, so it can hold Markdown, headers,
|
|
88
|
+
`=` signs, anything. Add an `INIT PROMPT:` section to override the prompt behind `opo`
|
|
89
|
+
(title + description) and/or a `REVIEW PROMPT:` section to override the one behind
|
|
90
|
+
`opo review`. Either section is optional; whichever is missing keeps the built-in default.
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
OPO_PROVIDER=ollama
|
|
94
|
+
OPO_MODEL=gemma4:26b
|
|
95
|
+
OPO_API_KEY=
|
|
96
|
+
OPO_HOST=http://localhost:11434
|
|
97
|
+
OPO_TEMPERATURE=0.0
|
|
98
|
+
OPO_KEEP_ALIVE=5m
|
|
99
|
+
OPO_BASE=main
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
INIT PROMPT:
|
|
103
|
+
You are a Senior Principal Engineer. Write a terse PR title and description in
|
|
104
|
+
Conventional Commits style. No emoji, no checklists.
|
|
105
|
+
|
|
106
|
+
REVIEW PROMPT:
|
|
107
|
+
Summarize what changed since the last review in one short paragraph.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`opo config set`/`opo config describe` only ever touch the `OPO_KEY=value` part above the
|
|
111
|
+
`---` line — the prompt section is left exactly as you wrote it, and `opo config describe`
|
|
112
|
+
tells you which of `init`/`review` are currently overridden.
|
|
113
|
+
|
|
114
|
+
### Configuration keys
|
|
115
|
+
|
|
116
|
+
| Key | Env var | Default | Description |
|
|
117
|
+
| --- | ------- | ------- | ----------- |
|
|
118
|
+
| `provider` | `OPO_PROVIDER` | `ollama` | `ollama` · `openai` · `anthropic` · `gemini` · `openrouter` · `deepseek` · `llamacpp` · `mlx` |
|
|
119
|
+
| `model` | `OPO_MODEL` | _(empty)_ | Model name — set via `opo setup` or `opo config set model <name>` |
|
|
120
|
+
| `api_key` | `OPO_API_KEY` | _(empty)_ | API key for cloud providers |
|
|
121
|
+
| `host` | `OPO_HOST` | `http://localhost:11434` | Base URL — Ollama default; set to a custom endpoint when needed |
|
|
122
|
+
| `temperature` | `OPO_TEMPERATURE` | `0.0` | Sampling temperature (`0.0` = deterministic) |
|
|
123
|
+
| `keep_alive` | `OPO_KEEP_ALIVE` | `5m` | Ollama VRAM keep-alive (`0s` = unload immediately, `5m` = keep warm) |
|
|
124
|
+
| `base` | `OPO_BASE` | `main` | Default base branch to diff against |
|
|
125
|
+
|
|
126
|
+
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).
|
|
127
|
+
|
|
128
|
+
### Example: switch to Google Gemini (free tier)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
opo config set provider gemini
|
|
132
|
+
opo config set model <your-chosen-model>
|
|
133
|
+
opo config set api_key <your-gemini-api-key>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
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.
|
|
137
|
+
|
|
138
|
+
### Example: unload Ollama from VRAM after each request
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
opo config set keep_alive 0s
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Models
|
|
145
|
+
|
|
146
|
+
List available models for the current or a specified provider:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
opo models
|
|
150
|
+
opo models --provider anthropic
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
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>`.
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "openpurr"
|
|
3
|
+
version = "0.8.5"
|
|
4
|
+
description = "CLI tool to generate PR titles, descriptions, and post-review change summaries using a local or cloud LLM."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"typer>=0.12.0",
|
|
9
|
+
"rich>=13.7.0",
|
|
10
|
+
"httpx>=0.27.0",
|
|
11
|
+
"openai>=1.0.0",
|
|
12
|
+
"anthropic>=0.122.0",
|
|
13
|
+
"questionary>=2.0.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
opo = "openpurr.main:app"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["hatchling"]
|
|
21
|
+
build-backend = "hatchling.build"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["src/openpurr"]
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = ["pytest>=8.0.0"]
|
|
28
|
+
|
|
29
|
+
[tool.pytest.ini_options]
|
|
30
|
+
testpaths = ["tests"]
|