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.
@@ -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
@@ -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
+ [![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)
21
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](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>`.
@@ -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
+ [![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)
8
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](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"]
@@ -0,0 +1,3 @@
1
+ """openpurr — PR text generator CLI."""
2
+
3
+ __version__ = "0.1.0"