semantic-diff 0.1.0__tar.gz → 0.3.0__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.
- semantic_diff-0.3.0/LICENSE +21 -0
- semantic_diff-0.3.0/PKG-INFO +269 -0
- semantic_diff-0.3.0/README.md +232 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/pyproject.toml +21 -3
- semantic_diff-0.3.0/semantic_diff/analyzers/llm_analyzer.py +312 -0
- semantic_diff-0.3.0/semantic_diff/change_set.py +49 -0
- semantic_diff-0.3.0/semantic_diff/cli.py +469 -0
- semantic_diff-0.3.0/semantic_diff/diff_pack.py +156 -0
- semantic_diff-0.3.0/semantic_diff/formatters/console_formatter.py +300 -0
- semantic_diff-0.3.0/semantic_diff/formatters/markdown_formatter.py +215 -0
- semantic_diff-0.3.0/semantic_diff/gitlab_env.py +34 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff/models.py +60 -9
- semantic_diff-0.3.0/semantic_diff/parsers/git_parser.py +229 -0
- semantic_diff-0.3.0/semantic_diff/prompt_parts.py +114 -0
- semantic_diff-0.3.0/semantic_diff/providers/__init__.py +55 -0
- semantic_diff-0.3.0/semantic_diff/providers/anthropic_provider.py +48 -0
- semantic_diff-0.3.0/semantic_diff/providers/base.py +121 -0
- semantic_diff-0.3.0/semantic_diff/providers/factory.py +70 -0
- semantic_diff-0.3.0/semantic_diff/providers/openai_compat.py +176 -0
- semantic_diff-0.3.0/semantic_diff/providers/retrying.py +103 -0
- semantic_diff-0.3.0/semantic_diff/review/__init__.py +4 -0
- semantic_diff-0.3.0/semantic_diff/review/agent.py +313 -0
- semantic_diff-0.3.0/semantic_diff/review/markdown.py +124 -0
- semantic_diff-0.3.0/semantic_diff/review/report.py +70 -0
- semantic_diff-0.3.0/semantic_diff/review/tools.py +310 -0
- semantic_diff-0.3.0/semantic_diff.egg-info/PKG-INFO +269 -0
- semantic_diff-0.3.0/semantic_diff.egg-info/SOURCES.txt +51 -0
- semantic_diff-0.3.0/semantic_diff.egg-info/entry_points.txt +2 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff.egg-info/requires.txt +9 -1
- semantic_diff-0.3.0/tests/test_action.py +141 -0
- semantic_diff-0.3.0/tests/test_change_set.py +158 -0
- semantic_diff-0.3.0/tests/test_cli.py +391 -0
- semantic_diff-0.3.0/tests/test_cli_mr.py +353 -0
- semantic_diff-0.3.0/tests/test_cli_review.py +174 -0
- semantic_diff-0.3.0/tests/test_converse.py +398 -0
- semantic_diff-0.3.0/tests/test_diff_pack.py +294 -0
- semantic_diff-0.3.0/tests/test_formatters.py +585 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/tests/test_git_parser.py +367 -339
- semantic_diff-0.3.0/tests/test_git_parser_range.py +203 -0
- semantic_diff-0.3.0/tests/test_llm_analyzer.py +549 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/tests/test_models.py +55 -47
- semantic_diff-0.3.0/tests/test_prompt_parts.py +102 -0
- semantic_diff-0.3.0/tests/test_providers.py +622 -0
- semantic_diff-0.3.0/tests/test_review_agent.py +669 -0
- semantic_diff-0.3.0/tests/test_review_markdown.py +306 -0
- semantic_diff-0.3.0/tests/test_review_tools.py +475 -0
- semantic_diff-0.1.0/PKG-INFO +0 -131
- semantic_diff-0.1.0/README.md +0 -103
- semantic_diff-0.1.0/semantic_diff/analyzers/llm_analyzer.py +0 -413
- semantic_diff-0.1.0/semantic_diff/cli.py +0 -86
- semantic_diff-0.1.0/semantic_diff/formatters/console_formatter.py +0 -165
- semantic_diff-0.1.0/semantic_diff/parsers/git_parser.py +0 -208
- semantic_diff-0.1.0/semantic_diff.egg-info/PKG-INFO +0 -131
- semantic_diff-0.1.0/semantic_diff.egg-info/SOURCES.txt +0 -20
- semantic_diff-0.1.0/semantic_diff.egg-info/entry_points.txt +0 -2
- semantic_diff-0.1.0/tests/test_llm_analyzer.py +0 -584
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff/__init__.py +0 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff/analyzers/__init__.py +0 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff/formatters/__init__.py +0 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff/parsers/__init__.py +0 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff.egg-info/dependency_links.txt +0 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/semantic_diff.egg-info/top_level.txt +0 -0
- {semantic_diff-0.1.0 → semantic_diff-0.3.0}/setup.cfg +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kenaz GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: semantic-diff
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: AI-powered semantic analysis of git commits
|
|
5
|
+
Author-email: Kenaz GmbH <hello@kenaz.ai>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tkenaz/semantic_diff
|
|
8
|
+
Project-URL: Repository, https://github.com/tkenaz/semantic_diff
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: anthropic>=0.40.0
|
|
19
|
+
Requires-Dist: click>=8.1.0
|
|
20
|
+
Requires-Dist: rich>=13.0.0
|
|
21
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
22
|
+
Requires-Dist: gitpython>=3.1.58
|
|
23
|
+
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai>=1.40.0; extra == "openai"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: openai>=1.40.0; extra == "dev"
|
|
33
|
+
Requires-Dist: mutmut>=3.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pyyaml; extra == "dev"
|
|
35
|
+
Requires-Dist: httpx; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# semantic-diff
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/semantic-diff/)
|
|
41
|
+
[](https://opensource.org/licenses/MIT)
|
|
42
|
+
|
|
43
|
+
AI-powered semantic analysis of git commits. Goes beyond `git diff` to show **intent**, **impact**, **risk**, and **review questions**.
|
|
44
|
+
|
|
45
|
+
## What it does
|
|
46
|
+
|
|
47
|
+
Regular `git diff` shows *what* changed. `semantic-diff` shows:
|
|
48
|
+
|
|
49
|
+
| | git diff | semantic-diff |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| **What** | Lines added/removed | Same |
|
|
52
|
+
| **Why** | - | Intent behind the change |
|
|
53
|
+
| **Risk** | - | What could break, edge cases |
|
|
54
|
+
| **Impact** | - | Direct and indirect effects |
|
|
55
|
+
| **Review** | - | Questions to ask the author |
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install semantic-diff
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Set your API key
|
|
67
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
68
|
+
|
|
69
|
+
# Analyze a commit
|
|
70
|
+
semantic-diff HEAD
|
|
71
|
+
|
|
72
|
+
# Enable automatic analysis before every push
|
|
73
|
+
semantic-diff init
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
That's it! Now every `git push` will automatically analyze your commits and save reports.
|
|
77
|
+
|
|
78
|
+
## How It Works
|
|
79
|
+
|
|
80
|
+
### Manual Analysis
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Analyze HEAD commit
|
|
84
|
+
semantic-diff
|
|
85
|
+
|
|
86
|
+
# Analyze specific commit
|
|
87
|
+
semantic-diff abc123
|
|
88
|
+
|
|
89
|
+
# Save report to file
|
|
90
|
+
semantic-diff HEAD --save
|
|
91
|
+
|
|
92
|
+
# Output as JSON
|
|
93
|
+
semantic-diff HEAD --json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Automatic Analysis (Pre-Push Hook)
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Install the hook (once per project)
|
|
100
|
+
semantic-diff init
|
|
101
|
+
|
|
102
|
+
# Now every push triggers analysis automatically
|
|
103
|
+
git push # → runs semantic-diff, saves report, then pushes
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Reports
|
|
107
|
+
|
|
108
|
+
Reports are saved to `semantic_diff_reports/` in your project root:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
my-project/
|
|
112
|
+
├── semantic_diff_reports/
|
|
113
|
+
│ ├── abc12345_20240115_143022.md
|
|
114
|
+
│ ├── def67890_20240115_152341.md
|
|
115
|
+
│ └── ...
|
|
116
|
+
├── src/
|
|
117
|
+
└── ...
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Each report includes:
|
|
121
|
+
- Commit metadata
|
|
122
|
+
- Intent analysis with confidence score
|
|
123
|
+
- Impact map (direct and indirect effects)
|
|
124
|
+
- Risk assessment with mitigations
|
|
125
|
+
- Review questions prioritized by importance
|
|
126
|
+
|
|
127
|
+
### GitHub Action
|
|
128
|
+
|
|
129
|
+
Add to your workflow to analyze PRs automatically:
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
# .github/workflows/semantic-diff.yml
|
|
133
|
+
name: Semantic Diff
|
|
134
|
+
|
|
135
|
+
on: [pull_request]
|
|
136
|
+
|
|
137
|
+
jobs:
|
|
138
|
+
analyze:
|
|
139
|
+
runs-on: ubuntu-latest
|
|
140
|
+
steps:
|
|
141
|
+
- uses: actions/checkout@v4
|
|
142
|
+
with:
|
|
143
|
+
fetch-depth: 0
|
|
144
|
+
|
|
145
|
+
- uses: tkenaz/semantic_diff@v0.3.0
|
|
146
|
+
with:
|
|
147
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
148
|
+
comment_on_pr: true
|
|
149
|
+
fail_on_risk: high # optional: block merge on high risk
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Example Output
|
|
153
|
+
|
|
154
|
+
### Analysis Overview
|
|
155
|
+
|
|
156
|
+

|
|
157
|
+
|
|
158
|
+
*Intent analysis with 98% confidence, risk assessment, and review questions*
|
|
159
|
+
|
|
160
|
+
### What It Finds
|
|
161
|
+
|
|
162
|
+

|
|
163
|
+
|
|
164
|
+
*Real example: semantic-diff analyzing itself found XSS vulnerabilities, CI issues, and security misconfigurations*
|
|
165
|
+
|
|
166
|
+
## Configuration
|
|
167
|
+
|
|
168
|
+
Create a `.env` file in your project or set environment variables:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Required
|
|
172
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
173
|
+
|
|
174
|
+
# Optional
|
|
175
|
+
SEMANTIC_DIFF_MODEL=claude-sonnet-5-5 # or claude-opus-5-5
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### OpenAI-compatible gateways
|
|
179
|
+
|
|
180
|
+
Any gateway that speaks the OpenAI Chat Completions protocol (LiteLLM, vLLM,
|
|
181
|
+
corporate LLM hubs, OpenAI itself) works through the `openai` provider:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pip install 'semantic-diff[openai]'
|
|
185
|
+
|
|
186
|
+
SEMANTIC_DIFF_PROVIDER=openai
|
|
187
|
+
SEMANTIC_DIFF_BASE_URL=https://your-gateway.example/v1
|
|
188
|
+
SEMANTIC_DIFF_API_KEY=...
|
|
189
|
+
SEMANTIC_DIFF_MODEL=claude-sonnet-4.6 # required: model ids differ per gateway
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Retries (rate limits honour `Retry-After`, timeouts, 5xx) are handled once for
|
|
193
|
+
all providers: `SEMANTIC_DIFF_MAX_RETRIES` (attempts, default 3) and
|
|
194
|
+
`SEMANTIC_DIFF_MAX_WAIT` (seconds, default 30).
|
|
195
|
+
|
|
196
|
+
### Diff budget
|
|
197
|
+
|
|
198
|
+
Large changes are not cut blindly. Diffs are packed into a token budget
|
|
199
|
+
(`--max-diff-tokens` / `SEMANTIC_DIFF_MAX_DIFF_TOKENS`, default 60,000):
|
|
200
|
+
code first, then migrations, CI/infra, tests, docs. A file that does not fit
|
|
201
|
+
whole is trimmed at hunk boundaries, never mid-line. Lock files, generated
|
|
202
|
+
output and binaries are listed but never shown. The model is told which files
|
|
203
|
+
it did not see, and every report states the coverage, e.g.
|
|
204
|
+
`Diff coverage: 87% of changed lines` plus the trimmed/not-shown files.
|
|
205
|
+
|
|
206
|
+
### Agentic review
|
|
207
|
+
|
|
208
|
+
`semantic-diff review` goes further than `mr`: instead of one call over the
|
|
209
|
+
diff, an agent reads around the change - whole functions, callers, the file
|
|
210
|
+
before the change, history - with read-only tools, and submits findings tied
|
|
211
|
+
to lines.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
semantic-diff review --base origin/dev --save
|
|
215
|
+
semantic-diff review --gitlab-env --json # inside a GitLab MR pipeline
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- Tools read git objects at the merge request's head and base, never the
|
|
219
|
+
working tree: uncommitted files are invisible to the model.
|
|
220
|
+
- Every review is bounded: `--max-turns` (`SEMANTIC_DIFF_MAX_TURNS`, default
|
|
221
|
+
25), `--max-input-tokens` (`SEMANTIC_DIFF_MAX_INPUT_TOKENS`, default
|
|
222
|
+
1,000,000, cached tokens included) and the diff shown up front
|
|
223
|
+
(`--max-diff-tokens` / `SEMANTIC_DIFF_REVIEW_DIFF_TOKENS`, default 30,000).
|
|
224
|
+
When a limit is reached the model gets one last turn to submit what it has,
|
|
225
|
+
and the report says which limit cut the review short.
|
|
226
|
+
- Each finding is checked in code: does the cited line exist (after the
|
|
227
|
+
change, or only before it), and did the model actually see the file. The
|
|
228
|
+
report flags findings that fail either check.
|
|
229
|
+
- Needs a provider with tool calling: `SEMANTIC_DIFF_PROVIDER=openai`
|
|
230
|
+
(any OpenAI-compatible gateway). Prompt caching is used where the gateway
|
|
231
|
+
supports it, so each turn re-reads the conversation from cache.
|
|
232
|
+
|
|
233
|
+
## Commands
|
|
234
|
+
|
|
235
|
+
| Command | Description |
|
|
236
|
+
|---------|-------------|
|
|
237
|
+
| `semantic-diff` | Analyze HEAD commit |
|
|
238
|
+
| `semantic-diff <hash>` | Analyze specific commit |
|
|
239
|
+
| `semantic-diff --save` | Save report to `semantic_diff_reports/` |
|
|
240
|
+
| `semantic-diff --json` | Output as JSON |
|
|
241
|
+
| `semantic-diff mr --base origin/main` | Analyze a whole merge request (merge-base..HEAD) |
|
|
242
|
+
| `semantic-diff mr --gitlab-env` | Same, inside a GitLab MR pipeline (reads `CI_MERGE_REQUEST_*`) |
|
|
243
|
+
| `--max-diff-tokens N` | Diff budget for the prompt (default 60000) |
|
|
244
|
+
| `--max-output-tokens N` | Room for the model's answer (default 32000; env `SEMANTIC_DIFF_MAX_OUTPUT_TOKENS`) |
|
|
245
|
+
| `semantic-diff review --base origin/main` | Agentic review of a merge request (see above) |
|
|
246
|
+
| `semantic-diff init` | Install pre-push hook |
|
|
247
|
+
| `semantic-diff uninstall` | Remove pre-push hook |
|
|
248
|
+
|
|
249
|
+
## Development
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
git clone https://github.com/tkenaz/semantic_diff.git
|
|
253
|
+
cd semantic_diff
|
|
254
|
+
pip install -e ".[dev]"
|
|
255
|
+
pytest
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Support
|
|
259
|
+
|
|
260
|
+
- **Bug reports & features**: [GitHub Issues](https://github.com/tkenaz/semantic_diff/issues)
|
|
261
|
+
- **Custom integrations or enterprise**: [hello@kenaz.ai](mailto:hello@kenaz.ai)
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT — [Kenaz GmbH](https://kenaz.ai)
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
*Built with 🖤 by [Kenaz GmbH](https://kenaz.ai) — AI tools for developers*
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# semantic-diff
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/semantic-diff/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
AI-powered semantic analysis of git commits. Goes beyond `git diff` to show **intent**, **impact**, **risk**, and **review questions**.
|
|
7
|
+
|
|
8
|
+
## What it does
|
|
9
|
+
|
|
10
|
+
Regular `git diff` shows *what* changed. `semantic-diff` shows:
|
|
11
|
+
|
|
12
|
+
| | git diff | semantic-diff |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| **What** | Lines added/removed | Same |
|
|
15
|
+
| **Why** | - | Intent behind the change |
|
|
16
|
+
| **Risk** | - | What could break, edge cases |
|
|
17
|
+
| **Impact** | - | Direct and indirect effects |
|
|
18
|
+
| **Review** | - | Questions to ask the author |
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install semantic-diff
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Set your API key
|
|
30
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
31
|
+
|
|
32
|
+
# Analyze a commit
|
|
33
|
+
semantic-diff HEAD
|
|
34
|
+
|
|
35
|
+
# Enable automatic analysis before every push
|
|
36
|
+
semantic-diff init
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
That's it! Now every `git push` will automatically analyze your commits and save reports.
|
|
40
|
+
|
|
41
|
+
## How It Works
|
|
42
|
+
|
|
43
|
+
### Manual Analysis
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Analyze HEAD commit
|
|
47
|
+
semantic-diff
|
|
48
|
+
|
|
49
|
+
# Analyze specific commit
|
|
50
|
+
semantic-diff abc123
|
|
51
|
+
|
|
52
|
+
# Save report to file
|
|
53
|
+
semantic-diff HEAD --save
|
|
54
|
+
|
|
55
|
+
# Output as JSON
|
|
56
|
+
semantic-diff HEAD --json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Automatic Analysis (Pre-Push Hook)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Install the hook (once per project)
|
|
63
|
+
semantic-diff init
|
|
64
|
+
|
|
65
|
+
# Now every push triggers analysis automatically
|
|
66
|
+
git push # → runs semantic-diff, saves report, then pushes
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Reports
|
|
70
|
+
|
|
71
|
+
Reports are saved to `semantic_diff_reports/` in your project root:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
my-project/
|
|
75
|
+
├── semantic_diff_reports/
|
|
76
|
+
│ ├── abc12345_20240115_143022.md
|
|
77
|
+
│ ├── def67890_20240115_152341.md
|
|
78
|
+
│ └── ...
|
|
79
|
+
├── src/
|
|
80
|
+
└── ...
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Each report includes:
|
|
84
|
+
- Commit metadata
|
|
85
|
+
- Intent analysis with confidence score
|
|
86
|
+
- Impact map (direct and indirect effects)
|
|
87
|
+
- Risk assessment with mitigations
|
|
88
|
+
- Review questions prioritized by importance
|
|
89
|
+
|
|
90
|
+
### GitHub Action
|
|
91
|
+
|
|
92
|
+
Add to your workflow to analyze PRs automatically:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
# .github/workflows/semantic-diff.yml
|
|
96
|
+
name: Semantic Diff
|
|
97
|
+
|
|
98
|
+
on: [pull_request]
|
|
99
|
+
|
|
100
|
+
jobs:
|
|
101
|
+
analyze:
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@v4
|
|
105
|
+
with:
|
|
106
|
+
fetch-depth: 0
|
|
107
|
+
|
|
108
|
+
- uses: tkenaz/semantic_diff@v0.3.0
|
|
109
|
+
with:
|
|
110
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
111
|
+
comment_on_pr: true
|
|
112
|
+
fail_on_risk: high # optional: block merge on high risk
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Example Output
|
|
116
|
+
|
|
117
|
+
### Analysis Overview
|
|
118
|
+
|
|
119
|
+

|
|
120
|
+
|
|
121
|
+
*Intent analysis with 98% confidence, risk assessment, and review questions*
|
|
122
|
+
|
|
123
|
+
### What It Finds
|
|
124
|
+
|
|
125
|
+

|
|
126
|
+
|
|
127
|
+
*Real example: semantic-diff analyzing itself found XSS vulnerabilities, CI issues, and security misconfigurations*
|
|
128
|
+
|
|
129
|
+
## Configuration
|
|
130
|
+
|
|
131
|
+
Create a `.env` file in your project or set environment variables:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Required
|
|
135
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
136
|
+
|
|
137
|
+
# Optional
|
|
138
|
+
SEMANTIC_DIFF_MODEL=claude-sonnet-5-5 # or claude-opus-5-5
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### OpenAI-compatible gateways
|
|
142
|
+
|
|
143
|
+
Any gateway that speaks the OpenAI Chat Completions protocol (LiteLLM, vLLM,
|
|
144
|
+
corporate LLM hubs, OpenAI itself) works through the `openai` provider:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
pip install 'semantic-diff[openai]'
|
|
148
|
+
|
|
149
|
+
SEMANTIC_DIFF_PROVIDER=openai
|
|
150
|
+
SEMANTIC_DIFF_BASE_URL=https://your-gateway.example/v1
|
|
151
|
+
SEMANTIC_DIFF_API_KEY=...
|
|
152
|
+
SEMANTIC_DIFF_MODEL=claude-sonnet-4.6 # required: model ids differ per gateway
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Retries (rate limits honour `Retry-After`, timeouts, 5xx) are handled once for
|
|
156
|
+
all providers: `SEMANTIC_DIFF_MAX_RETRIES` (attempts, default 3) and
|
|
157
|
+
`SEMANTIC_DIFF_MAX_WAIT` (seconds, default 30).
|
|
158
|
+
|
|
159
|
+
### Diff budget
|
|
160
|
+
|
|
161
|
+
Large changes are not cut blindly. Diffs are packed into a token budget
|
|
162
|
+
(`--max-diff-tokens` / `SEMANTIC_DIFF_MAX_DIFF_TOKENS`, default 60,000):
|
|
163
|
+
code first, then migrations, CI/infra, tests, docs. A file that does not fit
|
|
164
|
+
whole is trimmed at hunk boundaries, never mid-line. Lock files, generated
|
|
165
|
+
output and binaries are listed but never shown. The model is told which files
|
|
166
|
+
it did not see, and every report states the coverage, e.g.
|
|
167
|
+
`Diff coverage: 87% of changed lines` plus the trimmed/not-shown files.
|
|
168
|
+
|
|
169
|
+
### Agentic review
|
|
170
|
+
|
|
171
|
+
`semantic-diff review` goes further than `mr`: instead of one call over the
|
|
172
|
+
diff, an agent reads around the change - whole functions, callers, the file
|
|
173
|
+
before the change, history - with read-only tools, and submits findings tied
|
|
174
|
+
to lines.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
semantic-diff review --base origin/dev --save
|
|
178
|
+
semantic-diff review --gitlab-env --json # inside a GitLab MR pipeline
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- Tools read git objects at the merge request's head and base, never the
|
|
182
|
+
working tree: uncommitted files are invisible to the model.
|
|
183
|
+
- Every review is bounded: `--max-turns` (`SEMANTIC_DIFF_MAX_TURNS`, default
|
|
184
|
+
25), `--max-input-tokens` (`SEMANTIC_DIFF_MAX_INPUT_TOKENS`, default
|
|
185
|
+
1,000,000, cached tokens included) and the diff shown up front
|
|
186
|
+
(`--max-diff-tokens` / `SEMANTIC_DIFF_REVIEW_DIFF_TOKENS`, default 30,000).
|
|
187
|
+
When a limit is reached the model gets one last turn to submit what it has,
|
|
188
|
+
and the report says which limit cut the review short.
|
|
189
|
+
- Each finding is checked in code: does the cited line exist (after the
|
|
190
|
+
change, or only before it), and did the model actually see the file. The
|
|
191
|
+
report flags findings that fail either check.
|
|
192
|
+
- Needs a provider with tool calling: `SEMANTIC_DIFF_PROVIDER=openai`
|
|
193
|
+
(any OpenAI-compatible gateway). Prompt caching is used where the gateway
|
|
194
|
+
supports it, so each turn re-reads the conversation from cache.
|
|
195
|
+
|
|
196
|
+
## Commands
|
|
197
|
+
|
|
198
|
+
| Command | Description |
|
|
199
|
+
|---------|-------------|
|
|
200
|
+
| `semantic-diff` | Analyze HEAD commit |
|
|
201
|
+
| `semantic-diff <hash>` | Analyze specific commit |
|
|
202
|
+
| `semantic-diff --save` | Save report to `semantic_diff_reports/` |
|
|
203
|
+
| `semantic-diff --json` | Output as JSON |
|
|
204
|
+
| `semantic-diff mr --base origin/main` | Analyze a whole merge request (merge-base..HEAD) |
|
|
205
|
+
| `semantic-diff mr --gitlab-env` | Same, inside a GitLab MR pipeline (reads `CI_MERGE_REQUEST_*`) |
|
|
206
|
+
| `--max-diff-tokens N` | Diff budget for the prompt (default 60000) |
|
|
207
|
+
| `--max-output-tokens N` | Room for the model's answer (default 32000; env `SEMANTIC_DIFF_MAX_OUTPUT_TOKENS`) |
|
|
208
|
+
| `semantic-diff review --base origin/main` | Agentic review of a merge request (see above) |
|
|
209
|
+
| `semantic-diff init` | Install pre-push hook |
|
|
210
|
+
| `semantic-diff uninstall` | Remove pre-push hook |
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
git clone https://github.com/tkenaz/semantic_diff.git
|
|
216
|
+
cd semantic_diff
|
|
217
|
+
pip install -e ".[dev]"
|
|
218
|
+
pytest
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Support
|
|
222
|
+
|
|
223
|
+
- **Bug reports & features**: [GitHub Issues](https://github.com/tkenaz/semantic_diff/issues)
|
|
224
|
+
- **Custom integrations or enterprise**: [hello@kenaz.ai](mailto:hello@kenaz.ai)
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT — [Kenaz GmbH](https://kenaz.ai)
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
*Built with 🖤 by [Kenaz GmbH](https://kenaz.ai) — AI tools for developers*
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "semantic-diff"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "AI-powered semantic analysis of git commits"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -25,20 +25,28 @@ dependencies = [
|
|
|
25
25
|
"click>=8.1.0",
|
|
26
26
|
"rich>=13.0.0",
|
|
27
27
|
"python-dotenv>=1.0.0",
|
|
28
|
-
"gitpython>=3.1.
|
|
28
|
+
"gitpython>=3.1.58", # CVE-2026-42215, -44243 and later fixes
|
|
29
29
|
"pydantic>=2.0.0",
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.optional-dependencies]
|
|
33
|
+
openai = [
|
|
34
|
+
"openai>=1.40.0",
|
|
35
|
+
]
|
|
33
36
|
dev = [
|
|
34
37
|
"pytest>=7.0.0",
|
|
35
38
|
"pytest-cov>=4.0.0",
|
|
36
39
|
"black>=23.0.0",
|
|
37
40
|
"ruff>=0.1.0",
|
|
41
|
+
"mypy>=1.0.0",
|
|
42
|
+
"openai>=1.40.0",
|
|
43
|
+
"mutmut>=3.0.0",
|
|
44
|
+
"pyyaml", # tests execute action.yml steps
|
|
45
|
+
"httpx", # tests build SDK errors from real responses; newer SDKs ship httpx2
|
|
38
46
|
]
|
|
39
47
|
|
|
40
48
|
[project.scripts]
|
|
41
|
-
semantic-diff = "semantic_diff.cli:
|
|
49
|
+
semantic-diff = "semantic_diff.cli:cli"
|
|
42
50
|
|
|
43
51
|
[project.urls]
|
|
44
52
|
Homepage = "https://github.com/tkenaz/semantic_diff"
|
|
@@ -53,5 +61,15 @@ target-version = ["py310", "py311", "py312"]
|
|
|
53
61
|
|
|
54
62
|
[tool.ruff]
|
|
55
63
|
line-length = 100
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
56
66
|
select = ["E", "F", "I", "N", "W"]
|
|
57
67
|
ignore = ["E501"]
|
|
68
|
+
|
|
69
|
+
[tool.mutmut]
|
|
70
|
+
paths_to_mutate = ["semantic_diff/"]
|
|
71
|
+
tests_dir = ["tests/"]
|
|
72
|
+
also_copy = ["action.yml", "CHANGELOG.md", "pyproject.toml"]
|
|
73
|
+
|
|
74
|
+
[tool.pytest.ini_options]
|
|
75
|
+
testpaths = ["tests"]
|