diffron 0.1.0__tar.gz → 0.1.2__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.
Files changed (50) hide show
  1. {diffron-0.1.0 → diffron-0.1.2}/MANIFEST.in +17 -19
  2. {diffron-0.1.0/diffron.egg-info → diffron-0.1.2}/PKG-INFO +8 -8
  3. {diffron-0.1.0 → diffron-0.1.2}/README.md +7 -7
  4. {diffron-0.1.0 → diffron-0.1.2}/diffron/git_hooks.py +1 -1
  5. diffron-0.1.2/diffron/hooks/prepare-commit-msg +13 -0
  6. diffron-0.1.2/diffron/hooks/prepare-commit-msg-global.py +59 -0
  7. diffron-0.1.2/diffron/hooks/prepare-commit-msg.py +101 -0
  8. {diffron-0.1.0 → diffron-0.1.2/diffron.egg-info}/PKG-INFO +8 -8
  9. {diffron-0.1.0 → diffron-0.1.2}/diffron.egg-info/SOURCES.txt +7 -0
  10. {diffron-0.1.0 → diffron-0.1.2}/diffron.egg-info/top_level.txt +1 -0
  11. {diffron-0.1.0 → diffron-0.1.2}/docs/HOOKS.md +109 -7
  12. {diffron-0.1.0 → diffron-0.1.2}/docs/SETUP.md +1 -1
  13. diffron-0.1.2/hooks/aipr.py +96 -0
  14. diffron-0.1.2/hooks/prepare-commit-msg +13 -0
  15. diffron-0.1.2/hooks/prepare-commit-msg-global.py +59 -0
  16. diffron-0.1.2/hooks/prepare-commit-msg.py +101 -0
  17. {diffron-0.1.0 → diffron-0.1.2}/pyproject.toml +3 -2
  18. {diffron-0.1.0 → diffron-0.1.2}/setup.py +64 -64
  19. {diffron-0.1.0 → diffron-0.1.2}/CHANGELOG.md +0 -0
  20. {diffron-0.1.0 → diffron-0.1.2}/LICENSE +0 -0
  21. {diffron-0.1.0 → diffron-0.1.2}/diffron/__init__.py +0 -0
  22. {diffron-0.1.0 → diffron-0.1.2}/diffron/__init__.pyi +0 -0
  23. {diffron-0.1.0 → diffron-0.1.2}/diffron/cli.py +0 -0
  24. {diffron-0.1.0 → diffron-0.1.2}/diffron/cli.pyi +0 -0
  25. {diffron-0.1.0 → diffron-0.1.2}/diffron/client.py +0 -0
  26. {diffron-0.1.0 → diffron-0.1.2}/diffron/client.pyi +0 -0
  27. {diffron-0.1.0 → diffron-0.1.2}/diffron/commit_gen.py +0 -0
  28. {diffron-0.1.0 → diffron-0.1.2}/diffron/commit_gen.pyi +0 -0
  29. {diffron-0.1.0 → diffron-0.1.2}/diffron/git_hooks.pyi +0 -0
  30. {diffron-0.1.0 → diffron-0.1.2/diffron}/hooks/aipr.py +0 -0
  31. {diffron-0.1.0 → diffron-0.1.2}/diffron/lemonade.py +0 -0
  32. {diffron-0.1.0 → diffron-0.1.2}/diffron/lemonade.pyi +0 -0
  33. {diffron-0.1.0 → diffron-0.1.2}/diffron/pr_gen.py +0 -0
  34. {diffron-0.1.0 → diffron-0.1.2}/diffron/pr_gen.pyi +0 -0
  35. {diffron-0.1.0 → diffron-0.1.2}/diffron/py.typed +0 -0
  36. {diffron-0.1.0 → diffron-0.1.2}/diffron/utils.py +0 -0
  37. {diffron-0.1.0 → diffron-0.1.2}/diffron/utils.pyi +0 -0
  38. {diffron-0.1.0 → diffron-0.1.2}/diffron.egg-info/dependency_links.txt +0 -0
  39. {diffron-0.1.0 → diffron-0.1.2}/diffron.egg-info/entry_points.txt +0 -0
  40. {diffron-0.1.0 → diffron-0.1.2}/diffron.egg-info/requires.txt +0 -0
  41. {diffron-0.1.0 → diffron-0.1.2}/docs/PLAN.md +0 -0
  42. {diffron-0.1.0 → diffron-0.1.2}/docs/PYPI_RELEASE.md +0 -0
  43. {diffron-0.1.0 → diffron-0.1.2}/docs/USAGE.md +0 -0
  44. {diffron-0.1.0 → diffron-0.1.2}/requirements.txt +0 -0
  45. {diffron-0.1.0 → diffron-0.1.2}/setup.cfg +0 -0
  46. {diffron-0.1.0 → diffron-0.1.2}/tests/test_commit_gen.py +0 -0
  47. {diffron-0.1.0 → diffron-0.1.2}/tests/test_git_hooks.py +0 -0
  48. {diffron-0.1.0 → diffron-0.1.2}/tests/test_lemonade.py +0 -0
  49. {diffron-0.1.0 → diffron-0.1.2}/tests/test_pr_gen.py +0 -0
  50. {diffron-0.1.0 → diffron-0.1.2}/tests/test_utils.py +0 -0
@@ -1,19 +1,17 @@
1
- include LICENSE
2
- include README.md
3
- include CHANGELOG.md
4
- include requirements.txt
5
- include pyproject.toml
6
-
7
- recursive-include diffron *.py *.pyi py.typed
8
- recursive-include docs *.md
9
- recursive-include hooks *.py
10
-
11
- exclude tests
12
- exclude hooks/prepare-commit-msg
13
- exclude hooks/prepare-commit-msg-global.py
14
- exclude hooks/prepare-commit-msg.py
15
- exclude *.bat
16
- exclude *.code-workspace
17
- exclude sync-*.txt
18
- exclude .gitignore
19
- exclude .sorana
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include requirements.txt
5
+ include pyproject.toml
6
+
7
+ recursive-include diffron *.py *.pyi py.typed
8
+ recursive-include diffron/hooks *
9
+ recursive-include docs *.md
10
+
11
+ exclude tests
12
+ exclude hooks
13
+ exclude *.bat
14
+ exclude *.code-workspace
15
+ exclude sync-*.txt
16
+ exclude .gitignore
17
+ exclude .sorana
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffron
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Git commit message and PR description generator using Lemonade
5
5
  Home-page: https://github.com/diffron/diffron
6
6
  Author: Diffron Contributors
@@ -45,7 +45,7 @@ Dynamic: requires-python
45
45
 
46
46
  Git commit message and PR description generator using AMD Lemonade via lemonade-python-sdk.
47
47
 
48
- **This is a demo project showcasing the lemonade-python-sdk - submitted to the AMD Lemonade Developer Challenge 2026.**
48
+ **Diffron is a production-ready reference implementation of the lemonade-python-sdk submitted to the AMD Lemonade Developer Challenge 2026.**
49
49
 
50
50
  ![Version](https://img.shields.io/badge/version-0.1.0-blue)
51
51
  ![Python](https://img.shields.io/badge/python-3.9+-blue)
@@ -243,12 +243,12 @@ client = DiffronClient(model="your-model-name")
243
243
 
244
244
  | Project | Description |
245
245
  |---------|-------------|
246
- | **lemonade-python-sdk** | 🍋 **AMD Lemonade Challenge Submission** - Python SDK for AMD Lemonade API |
247
- | **Diffron** | Demo project showcasing lemonade-python-sdk (this repo) |
248
- | **Aicono** | AI Assistant (Desktop App) |
249
- | **TabNeuron** | Browser Connector / Memory |
250
- | **Sorana** | Advanced AI Interface |
251
- | **RyzenPilot** | Hardware Optimization |
246
+ | [**lemonade-python-sdk**](https://github.com/Tetramatrix/lemonade-python-sdk) | 🍋 **AMD Lemonade Challenge Submission** - Python SDK for AMD Lemonade API |
247
+ | [**Diffron**](https://pypi.org/project/diffron/) | Production-ready reference implementation using lemonade-python-sdk (this repo) |
248
+ | [**Aicono**](https://tetramatrix.github.io/Aicono/) | AI Assistant (Desktop App) |
249
+ | [**TabNeuron**](https://tetramatrix.github.io/TabNeuron/) | Browser Connector / Memory |
250
+ | [**Sorana**](https://tetramatrix.github.io/Sorana/) | Advanced AI Interface |
251
+ | [**RyzenZPilot**](https://tetramatrix.github.io/RyzenZPilot/) | Hardware Optimization |
252
252
 
253
253
  **Note:** Lemonade is AMD's local LLM server for Ryzen AI PCs. Diffron uses `lemonade-python-sdk` to communicate with Lemonade's API.
254
254
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Git commit message and PR description generator using AMD Lemonade via lemonade-python-sdk.
4
4
 
5
- **This is a demo project showcasing the lemonade-python-sdk - submitted to the AMD Lemonade Developer Challenge 2026.**
5
+ **Diffron is a production-ready reference implementation of the lemonade-python-sdk submitted to the AMD Lemonade Developer Challenge 2026.**
6
6
 
7
7
  ![Version](https://img.shields.io/badge/version-0.1.0-blue)
8
8
  ![Python](https://img.shields.io/badge/python-3.9+-blue)
@@ -200,12 +200,12 @@ client = DiffronClient(model="your-model-name")
200
200
 
201
201
  | Project | Description |
202
202
  |---------|-------------|
203
- | **lemonade-python-sdk** | 🍋 **AMD Lemonade Challenge Submission** - Python SDK for AMD Lemonade API |
204
- | **Diffron** | Demo project showcasing lemonade-python-sdk (this repo) |
205
- | **Aicono** | AI Assistant (Desktop App) |
206
- | **TabNeuron** | Browser Connector / Memory |
207
- | **Sorana** | Advanced AI Interface |
208
- | **RyzenPilot** | Hardware Optimization |
203
+ | [**lemonade-python-sdk**](https://github.com/Tetramatrix/lemonade-python-sdk) | 🍋 **AMD Lemonade Challenge Submission** - Python SDK for AMD Lemonade API |
204
+ | [**Diffron**](https://pypi.org/project/diffron/) | Production-ready reference implementation using lemonade-python-sdk (this repo) |
205
+ | [**Aicono**](https://tetramatrix.github.io/Aicono/) | AI Assistant (Desktop App) |
206
+ | [**TabNeuron**](https://tetramatrix.github.io/TabNeuron/) | Browser Connector / Memory |
207
+ | [**Sorana**](https://tetramatrix.github.io/Sorana/) | Advanced AI Interface |
208
+ | [**RyzenZPilot**](https://tetramatrix.github.io/RyzenZPilot/) | Hardware Optimization |
209
209
 
210
210
  **Note:** Lemonade is AMD's local LLM server for Ryzen AI PCs. Diffron uses `lemonade-python-sdk` to communicate with Lemonade's API.
211
211
 
@@ -19,7 +19,7 @@ PYTHON_HOOK_NAME = "prepare-commit-msg.py"
19
19
 
20
20
  # Get the hooks directory (where the template hooks are stored)
21
21
  DIFFRON_PACKAGE_DIR = Path(__file__).parent
22
- HOOKS_TEMPLATE_DIR = DIFFRON_PACKAGE_DIR.parent / "hooks"
22
+ HOOKS_TEMPLATE_DIR = DIFFRON_PACKAGE_DIR / "hooks"
23
23
 
24
24
 
25
25
  def install_hooks(
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+ # Diffron prepare-commit-msg hook wrapper for Windows (Git Bash)
3
+ # This file should be placed in .git/hooks/prepare-commit-msg (no extension)
4
+
5
+ # Get the directory where this wrapper script is located (Unix-style path)
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+
8
+ # Convert Unix path to Windows path for Python using cygpath
9
+ # Git Bash on Windows uses /d/... but Windows needs D:\...
10
+ PYTHON_SCRIPT="$(cygpath -w "$SCRIPT_DIR/prepare-commit-msg.py" 2>/dev/null || echo "$SCRIPT_DIR/prepare-commit-msg.py")"
11
+
12
+ # Run the Python script
13
+ python "$PYTHON_SCRIPT" "$1" "$2"
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Diffron prepare-commit-msg Git hook.
4
+
5
+ Automatically generates commit messages using Lemonade LLM.
6
+ Installed globally by Diffron.
7
+ """
8
+
9
+ import sys
10
+ import os
11
+
12
+ # Import diffron from installed package (no path manipulation needed)
13
+ from diffron.commit_gen import generate_commit_message
14
+ from diffron.lemonade import is_lemonade_running
15
+
16
+
17
+ def main():
18
+ """Main hook entry point."""
19
+ # Parse arguments
20
+ if len(sys.argv) < 2:
21
+ print("Error: Missing commit message file argument", file=sys.stderr)
22
+ sys.exit(1)
23
+
24
+ commit_msg_file = sys.argv[1]
25
+ commit_source = sys.argv[2] if len(sys.argv) > 2 else ""
26
+
27
+ # Skip for merges, rebases, and amend commits
28
+ skip_sources = {"merge", "squash", "commit"}
29
+ if commit_source in skip_sources:
30
+ sys.exit(0)
31
+
32
+ # Check if Lemonade is running
33
+ if not is_lemonade_running():
34
+ # Silently exit if Lemonade not running - let user commit manually
35
+ sys.exit(0)
36
+
37
+ try:
38
+ # Generate commit message
39
+ commit_message = generate_commit_message()
40
+
41
+ if commit_message:
42
+ # Write to commit message file
43
+ with open(commit_msg_file, "w", encoding="utf-8") as f:
44
+ f.write(commit_message)
45
+ except Exception as e:
46
+ # Log error but don't block commit
47
+ error_msg = f"# Diffron error: {e}\n"
48
+ try:
49
+ with open(commit_msg_file, "a", encoding="utf-8") as f:
50
+ f.write(error_msg)
51
+ except Exception:
52
+ pass
53
+ sys.exit(0) # Don't block commit on error
54
+
55
+ sys.exit(0)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ main()
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Diffron prepare-commit-msg Git hook.
4
+
5
+ Automatically generates commit messages using Lemonade LLM.
6
+ Installed globally by Diffron.
7
+ """
8
+
9
+ import sys
10
+ import os
11
+ import urllib.request
12
+ import urllib.error
13
+
14
+ # Import diffron from installed package (no path manipulation needed)
15
+ from diffron.commit_gen import generate_commit_message
16
+ from diffron.lemonade import get_lemonade_url
17
+
18
+
19
+ def is_lemonade_api_responsive(url: str, timeout: float = 10.0) -> bool:
20
+ """
21
+ Check if Lemonade API actually responds (not just port open).
22
+
23
+ Args:
24
+ url: Lemonade server URL.
25
+ timeout: Request timeout in seconds (10s to allow for slow startup).
26
+
27
+ Returns:
28
+ True if API responds successfully.
29
+ """
30
+ try:
31
+ # Try /api/v1/models endpoint
32
+ models_url = f"{url}/api/v1/models"
33
+ req = urllib.request.Request(models_url)
34
+ with urllib.request.urlopen(req, timeout=timeout) as response:
35
+ if response.status == 200:
36
+ return True
37
+ except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError, OSError):
38
+ pass
39
+
40
+ # Fallback: try root endpoint
41
+ try:
42
+ req = urllib.request.Request(url)
43
+ with urllib.request.urlopen(req, timeout=timeout) as response:
44
+ return response.status == 200
45
+ except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError, OSError):
46
+ pass
47
+
48
+ return False
49
+
50
+
51
+ def main():
52
+ """Main hook entry point."""
53
+ # Parse arguments
54
+ if len(sys.argv) < 2:
55
+ print("Error: Missing commit message file argument", file=sys.stderr)
56
+ sys.exit(1)
57
+
58
+ commit_msg_file = sys.argv[1]
59
+ commit_source = sys.argv[2] if len(sys.argv) > 2 else ""
60
+
61
+ # Skip for merges, rebases, and amend commits
62
+ skip_sources = {"merge", "squash", "commit"}
63
+ if commit_source in skip_sources:
64
+ sys.exit(0)
65
+
66
+ # Change to the repository directory (where git commit was run)
67
+ repo_dir = os.path.dirname(os.path.abspath(commit_msg_file))
68
+ # Navigate up from .git/COMMIT_EDITMSG to repo root
69
+ if os.path.basename(repo_dir) == ".git":
70
+ repo_dir = os.path.dirname(repo_dir)
71
+ os.chdir(repo_dir)
72
+
73
+ # Get Lemonade URL and check if API is actually responsive
74
+ lemonade_url = get_lemonade_url()
75
+ if not is_lemonade_api_responsive(lemonade_url):
76
+ # Silently exit if Lemonade not responding - let user commit manually
77
+ sys.exit(0)
78
+
79
+ try:
80
+ # Generate commit message
81
+ commit_message = generate_commit_message()
82
+
83
+ if commit_message:
84
+ # Write to commit message file
85
+ with open(commit_msg_file, "w", encoding="utf-8") as f:
86
+ f.write(commit_message)
87
+ except Exception as e:
88
+ # Log error but don't block commit
89
+ error_msg = f"# Diffron error: {e}\n"
90
+ try:
91
+ with open(commit_msg_file, "a", encoding="utf-8") as f:
92
+ f.write(error_msg)
93
+ except Exception:
94
+ pass
95
+ sys.exit(0) # Don't block commit on error
96
+
97
+ sys.exit(0)
98
+
99
+
100
+ if __name__ == "__main__":
101
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffron
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Git commit message and PR description generator using Lemonade
5
5
  Home-page: https://github.com/diffron/diffron
6
6
  Author: Diffron Contributors
@@ -45,7 +45,7 @@ Dynamic: requires-python
45
45
 
46
46
  Git commit message and PR description generator using AMD Lemonade via lemonade-python-sdk.
47
47
 
48
- **This is a demo project showcasing the lemonade-python-sdk - submitted to the AMD Lemonade Developer Challenge 2026.**
48
+ **Diffron is a production-ready reference implementation of the lemonade-python-sdk submitted to the AMD Lemonade Developer Challenge 2026.**
49
49
 
50
50
  ![Version](https://img.shields.io/badge/version-0.1.0-blue)
51
51
  ![Python](https://img.shields.io/badge/python-3.9+-blue)
@@ -243,12 +243,12 @@ client = DiffronClient(model="your-model-name")
243
243
 
244
244
  | Project | Description |
245
245
  |---------|-------------|
246
- | **lemonade-python-sdk** | 🍋 **AMD Lemonade Challenge Submission** - Python SDK for AMD Lemonade API |
247
- | **Diffron** | Demo project showcasing lemonade-python-sdk (this repo) |
248
- | **Aicono** | AI Assistant (Desktop App) |
249
- | **TabNeuron** | Browser Connector / Memory |
250
- | **Sorana** | Advanced AI Interface |
251
- | **RyzenPilot** | Hardware Optimization |
246
+ | [**lemonade-python-sdk**](https://github.com/Tetramatrix/lemonade-python-sdk) | 🍋 **AMD Lemonade Challenge Submission** - Python SDK for AMD Lemonade API |
247
+ | [**Diffron**](https://pypi.org/project/diffron/) | Production-ready reference implementation using lemonade-python-sdk (this repo) |
248
+ | [**Aicono**](https://tetramatrix.github.io/Aicono/) | AI Assistant (Desktop App) |
249
+ | [**TabNeuron**](https://tetramatrix.github.io/TabNeuron/) | Browser Connector / Memory |
250
+ | [**Sorana**](https://tetramatrix.github.io/Sorana/) | Advanced AI Interface |
251
+ | [**RyzenZPilot**](https://tetramatrix.github.io/RyzenZPilot/) | Hardware Optimization |
252
252
 
253
253
  **Note:** Lemonade is AMD's local LLM server for Ryzen AI PCs. Diffron uses `lemonade-python-sdk` to communicate with Lemonade's API.
254
254
 
@@ -28,12 +28,19 @@ diffron.egg-info/dependency_links.txt
28
28
  diffron.egg-info/entry_points.txt
29
29
  diffron.egg-info/requires.txt
30
30
  diffron.egg-info/top_level.txt
31
+ diffron/hooks/aipr.py
32
+ diffron/hooks/prepare-commit-msg
33
+ diffron/hooks/prepare-commit-msg-global.py
34
+ diffron/hooks/prepare-commit-msg.py
31
35
  docs/HOOKS.md
32
36
  docs/PLAN.md
33
37
  docs/PYPI_RELEASE.md
34
38
  docs/SETUP.md
35
39
  docs/USAGE.md
36
40
  hooks/aipr.py
41
+ hooks/prepare-commit-msg
42
+ hooks/prepare-commit-msg-global.py
43
+ hooks/prepare-commit-msg.py
37
44
  tests/test_commit_gen.py
38
45
  tests/test_git_hooks.py
39
46
  tests/test_lemonade.py
@@ -1,5 +1,6 @@
1
1
  diffron
2
2
  dist
3
3
  docs
4
+ hooks
4
5
  internal
5
6
  mvp
@@ -164,13 +164,18 @@ C:\Users\YourName\.diffron-hooks\
164
164
  #### prepare-commit-msg (Shell Wrapper)
165
165
 
166
166
  ```bash
167
- #!/C:/Program Files/Git/usr/bin/sh.exe
168
- # Diffron prepare-commit-msg hook wrapper
167
+ #!/bin/sh
168
+ # Diffron prepare-commit-msg hook wrapper for Windows (Git Bash)
169
169
 
170
- # Get the directory where this wrapper script is located
171
- HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
172
- python "$HOOK_DIR/prepare-commit-msg.py" "$1" "$2"
173
- exit 0
170
+ # Get the directory where this wrapper script is located (Unix-style path)
171
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
172
+
173
+ # Convert Unix path to Windows path for Python using cygpath
174
+ # Git Bash on Windows uses /d/... but Windows needs D:\...
175
+ PYTHON_SCRIPT="$(cygpath -w "$SCRIPT_DIR/prepare-commit-msg.py" 2>/dev/null || echo "$SCRIPT_DIR/prepare-commit-msg.py")"
176
+
177
+ # Run the Python script
178
+ python "$PYTHON_SCRIPT" "$1" "$2"
174
179
  ```
175
180
 
176
181
  **Purpose:** Git for Windows executes this shell script, which delegates to Python.
@@ -179,6 +184,7 @@ exit 0
179
184
  - Git for Windows uses bundled Git Bash
180
185
  - Python may not be in Git Bash PATH
181
186
  - Wrapper ensures correct Python interpreter is used
187
+ - **cygpath** converts Unix-style paths (`/d/...`) to Windows paths (`D:\...`)
182
188
 
183
189
  #### prepare-commit-msg.py (Python Hook)
184
190
 
@@ -417,6 +423,102 @@ git commit -m "test"
417
423
 
418
424
  ---
419
425
 
426
+ ## Troubleshooting
427
+
428
+ ### Hooks werden nicht ausgeführt
429
+
430
+ **Symptom:** `git commit` öffnet den Editor ohne generierte Message.
431
+
432
+ **Ursache 1: Lokaler `core.hooksPath` überschreibt globalen Wert**
433
+
434
+ ```bash
435
+ # Prüfen ob lokaler hooksPath gesetzt ist
436
+ git config core.hooksPath
437
+
438
+ # Wenn Output leer ist ("core.hooksPath="), wurde er auf leer gesetzt
439
+ # Das deaktiviert Hooks komplett!
440
+
441
+ # Lösung: Lokalen Wert entfernen
442
+ git config --unset core.hooksPath
443
+
444
+ # Verifizieren: Sollte jetzt keinen Output geben
445
+ git config core.hooksPath
446
+ ```
447
+
448
+ **Ursache 2: Backup hat fehlerhafte Konfiguration wiederhergestellt**
449
+
450
+ Nach dem Einspielen eines Backups kann die `.git/config` einen leeren `core.hooksPath=` enthalten.
451
+
452
+ **Lösung nach Backup:**
453
+ ```bash
454
+ cd path/to/repo
455
+ git config --unset core.hooksPath
456
+ git commit # Testen ob Hook funktioniert
457
+ ```
458
+
459
+ **Ursache 3: Wrapper-Pfad-Konvertierung fehlgeschlagen**
460
+
461
+ Der Shell-Wrapper muss Unix-Pfade (`/d/...`) zu Windows-Pfaden (`D:\...`) konvertieren.
462
+
463
+ **Lösung:** Stelle sicher dass der Wrapper `cygpath` verwendet:
464
+ ```bash
465
+ #!/bin/sh
466
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
467
+ PYTHON_SCRIPT="$(cygpath -w "$SCRIPT_DIR/prepare-commit-msg.py" 2>/dev/null || echo "$SCRIPT_DIR/prepare-commit-msg.py")"
468
+ python "$PYTHON_SCRIPT" "$1" "$2"
469
+ ```
470
+
471
+ ### Lemonade antwortet nicht / ist langsam
472
+
473
+ **Symptom:** Hook schreibt keine Message, Commit erfordert manuelle Eingabe.
474
+
475
+ **Ursache:** Lemonade Server ist nicht erreichbar oder antwortet zu langsam.
476
+
477
+ **Lösung:**
478
+ 1. Prüfen ob Lemonade läuft: `http://localhost:8020` im Browser öffnen
479
+ 2. Hook-Timeout erhöhen (Standard: 10 Sekunden):
480
+ ```python
481
+ # In prepare-commit-msg.py
482
+ def is_lemonade_api_responsive(url: str, timeout: float = 10.0) -> bool:
483
+ ```
484
+
485
+ ### "No staged changes" Fehler im Log
486
+
487
+ **Symptom:** Hook wird ausgeführt, aber generiert keine Message.
488
+
489
+ **Ursache:** Der Hook prüft den git diff im aktuellen Arbeitsverzeichnis, nicht im Hook-Kontext.
490
+
491
+ **Lösung:** Der Hook wechselt automatisch ins Repository-Verzeichnis. Stelle sicher dass:
492
+ ```python
493
+ # Change to the repository directory
494
+ repo_dir = os.path.dirname(os.path.abspath(commit_msg_file))
495
+ if os.path.basename(repo_dir) == ".git":
496
+ repo_dir = os.path.dirname(repo_dir)
497
+ os.chdir(repo_dir)
498
+ ```
499
+
500
+ ### Globale vs. Lokale Hooks
501
+
502
+ **Frage:** Soll ich globale oder lokale Hooks verwenden?
503
+
504
+ **Empfehlung:** Verwende **globale Hooks** für konsistentes Verhalten:
505
+ ```bash
506
+ git config --global core.hooksPath "C:/Users/YourName/.diffron-hooks"
507
+ ```
508
+
509
+ **Vorteile:**
510
+ - Einmal installieren, überall verfügbar
511
+ - Backup von `.git/config` beeinflusst Hooks nicht
512
+ - Konsistentes Verhalten über alle Repositories
513
+
514
+ **Nach Backup prüfen:**
515
+ ```bash
516
+ git config --global core.hooksPath # Sollte Pfad anzeigen
517
+ git config core.hooksPath # Sollte leer sein (kein Output)
518
+ ```
519
+
520
+ ---
521
+
420
522
  ## Hook Lifecycle
421
523
 
422
524
  ### Installation
@@ -438,5 +540,5 @@ git commit -m "test"
438
540
 
439
541
  ---
440
542
 
441
- *Last updated: 2026-03-28*
543
+ *Last updated: 2026-03-29*
442
544
  *Version: 0.1.0*
@@ -2,7 +2,7 @@
2
2
 
3
3
  Complete installation guide for Diffron on Windows with AMD Lemonade and GitHub Desktop.
4
4
 
5
- **Diffron is a demo project showcasing the lemonade-python-sdk - submitted to the AMD Lemonade Developer Challenge 2026.**
5
+ **Diffron is a production-ready reference implementation of the lemonade-python-sdk submitted to the AMD Lemonade Developer Challenge 2026.**
6
6
 
7
7
  ---
8
8
 
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Diffron PR Description Generator (aipr).
4
+
5
+ Generates GitHub PR title and description from branch changes.
6
+ Run this script manually when ready to create a PR.
7
+
8
+ Usage:
9
+ python aipr.py [branch] [base]
10
+
11
+ branch: Branch to analyze (default: current branch)
12
+ base: Base branch to compare against (default: main/master)
13
+
14
+ Example:
15
+ python aipr.py feature/my-feature main
16
+ """
17
+
18
+ import sys
19
+ import os
20
+ import subprocess
21
+
22
+ # Add parent directory to path to import diffron
23
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
24
+
25
+ from diffron.pr_gen import generate_pr_description, create_github_pr
26
+ from diffron.lemonade import is_lemonade_running
27
+
28
+
29
+ def check_gh_cli() -> bool:
30
+ """Check if GitHub CLI is available."""
31
+ try:
32
+ result = subprocess.run(
33
+ ["gh", "--version"],
34
+ capture_output=True,
35
+ text=True,
36
+ timeout=10,
37
+ )
38
+ return result.returncode == 0
39
+ except (subprocess.SubprocessError, FileNotFoundError):
40
+ return False
41
+
42
+
43
+ def main():
44
+ """Main entry point."""
45
+ # Parse arguments
46
+ branch = sys.argv[1] if len(sys.argv) > 1 else None
47
+ base = sys.argv[2] if len(sys.argv) > 2 else None
48
+
49
+ # Check if Lemonade is running
50
+ if not is_lemonade_running():
51
+ print(
52
+ "Error: Lemonade server is not running.",
53
+ file=sys.stderr
54
+ )
55
+ print(
56
+ "Please start Lemonade first (e.g., 'lemonade serve').",
57
+ file=sys.stderr
58
+ )
59
+ sys.exit(1)
60
+
61
+ try:
62
+ # Generate PR description
63
+ print("Analyzing branch changes...")
64
+ pr = generate_pr_description(branch=branch, base=base)
65
+
66
+ # Output result
67
+ print("\n" + "=" * 60)
68
+ print(pr.format_output())
69
+ print("=" * 60 + "\n")
70
+
71
+ # Check if gh CLI is available
72
+ if check_gh_cli():
73
+ response = input("Create PR on GitHub now? (y/n): ").strip().lower()
74
+ if response in ("y", "yes"):
75
+ print("Creating PR...")
76
+ create_github_pr(branch=branch, base=base, auto_submit=True)
77
+ print("PR created successfully!")
78
+ else:
79
+ print(
80
+ "Tip: Install GitHub CLI (gh) to auto-create PRs: "
81
+ "https://cli.github.com/"
82
+ )
83
+
84
+ except ValueError as e:
85
+ print(f"Error: {e}", file=sys.stderr)
86
+ sys.exit(1)
87
+ except ConnectionError as e:
88
+ print(f"Connection error: {e}", file=sys.stderr)
89
+ sys.exit(1)
90
+ except KeyboardInterrupt:
91
+ print("\nCancelled.")
92
+ sys.exit(0)
93
+
94
+
95
+ if __name__ == "__main__":
96
+ main()
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+ # Diffron prepare-commit-msg hook wrapper for Windows (Git Bash)
3
+ # This file should be placed in .git/hooks/prepare-commit-msg (no extension)
4
+
5
+ # Get the directory where this wrapper script is located (Unix-style path)
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+
8
+ # Convert Unix path to Windows path for Python using cygpath
9
+ # Git Bash on Windows uses /d/... but Windows needs D:\...
10
+ PYTHON_SCRIPT="$(cygpath -w "$SCRIPT_DIR/prepare-commit-msg.py" 2>/dev/null || echo "$SCRIPT_DIR/prepare-commit-msg.py")"
11
+
12
+ # Run the Python script
13
+ python "$PYTHON_SCRIPT" "$1" "$2"
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Diffron prepare-commit-msg Git hook.
4
+
5
+ Automatically generates commit messages using Lemonade LLM.
6
+ Installed globally by Diffron.
7
+ """
8
+
9
+ import sys
10
+ import os
11
+
12
+ # Import diffron from installed package (no path manipulation needed)
13
+ from diffron.commit_gen import generate_commit_message
14
+ from diffron.lemonade import is_lemonade_running
15
+
16
+
17
+ def main():
18
+ """Main hook entry point."""
19
+ # Parse arguments
20
+ if len(sys.argv) < 2:
21
+ print("Error: Missing commit message file argument", file=sys.stderr)
22
+ sys.exit(1)
23
+
24
+ commit_msg_file = sys.argv[1]
25
+ commit_source = sys.argv[2] if len(sys.argv) > 2 else ""
26
+
27
+ # Skip for merges, rebases, and amend commits
28
+ skip_sources = {"merge", "squash", "commit"}
29
+ if commit_source in skip_sources:
30
+ sys.exit(0)
31
+
32
+ # Check if Lemonade is running
33
+ if not is_lemonade_running():
34
+ # Silently exit if Lemonade not running - let user commit manually
35
+ sys.exit(0)
36
+
37
+ try:
38
+ # Generate commit message
39
+ commit_message = generate_commit_message()
40
+
41
+ if commit_message:
42
+ # Write to commit message file
43
+ with open(commit_msg_file, "w", encoding="utf-8") as f:
44
+ f.write(commit_message)
45
+ except Exception as e:
46
+ # Log error but don't block commit
47
+ error_msg = f"# Diffron error: {e}\n"
48
+ try:
49
+ with open(commit_msg_file, "a", encoding="utf-8") as f:
50
+ f.write(error_msg)
51
+ except Exception:
52
+ pass
53
+ sys.exit(0) # Don't block commit on error
54
+
55
+ sys.exit(0)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ main()
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Diffron prepare-commit-msg Git hook.
4
+
5
+ Automatically generates commit messages using Lemonade LLM.
6
+ Installed globally by Diffron.
7
+ """
8
+
9
+ import sys
10
+ import os
11
+ import urllib.request
12
+ import urllib.error
13
+
14
+ # Import diffron from installed package (no path manipulation needed)
15
+ from diffron.commit_gen import generate_commit_message
16
+ from diffron.lemonade import get_lemonade_url
17
+
18
+
19
+ def is_lemonade_api_responsive(url: str, timeout: float = 10.0) -> bool:
20
+ """
21
+ Check if Lemonade API actually responds (not just port open).
22
+
23
+ Args:
24
+ url: Lemonade server URL.
25
+ timeout: Request timeout in seconds (10s to allow for slow startup).
26
+
27
+ Returns:
28
+ True if API responds successfully.
29
+ """
30
+ try:
31
+ # Try /api/v1/models endpoint
32
+ models_url = f"{url}/api/v1/models"
33
+ req = urllib.request.Request(models_url)
34
+ with urllib.request.urlopen(req, timeout=timeout) as response:
35
+ if response.status == 200:
36
+ return True
37
+ except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError, OSError):
38
+ pass
39
+
40
+ # Fallback: try root endpoint
41
+ try:
42
+ req = urllib.request.Request(url)
43
+ with urllib.request.urlopen(req, timeout=timeout) as response:
44
+ return response.status == 200
45
+ except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError, OSError):
46
+ pass
47
+
48
+ return False
49
+
50
+
51
+ def main():
52
+ """Main hook entry point."""
53
+ # Parse arguments
54
+ if len(sys.argv) < 2:
55
+ print("Error: Missing commit message file argument", file=sys.stderr)
56
+ sys.exit(1)
57
+
58
+ commit_msg_file = sys.argv[1]
59
+ commit_source = sys.argv[2] if len(sys.argv) > 2 else ""
60
+
61
+ # Skip for merges, rebases, and amend commits
62
+ skip_sources = {"merge", "squash", "commit"}
63
+ if commit_source in skip_sources:
64
+ sys.exit(0)
65
+
66
+ # Change to the repository directory (where git commit was run)
67
+ repo_dir = os.path.dirname(os.path.abspath(commit_msg_file))
68
+ # Navigate up from .git/COMMIT_EDITMSG to repo root
69
+ if os.path.basename(repo_dir) == ".git":
70
+ repo_dir = os.path.dirname(repo_dir)
71
+ os.chdir(repo_dir)
72
+
73
+ # Get Lemonade URL and check if API is actually responsive
74
+ lemonade_url = get_lemonade_url()
75
+ if not is_lemonade_api_responsive(lemonade_url):
76
+ # Silently exit if Lemonade not responding - let user commit manually
77
+ sys.exit(0)
78
+
79
+ try:
80
+ # Generate commit message
81
+ commit_message = generate_commit_message()
82
+
83
+ if commit_message:
84
+ # Write to commit message file
85
+ with open(commit_msg_file, "w", encoding="utf-8") as f:
86
+ f.write(commit_message)
87
+ except Exception as e:
88
+ # Log error but don't block commit
89
+ error_msg = f"# Diffron error: {e}\n"
90
+ try:
91
+ with open(commit_msg_file, "a", encoding="utf-8") as f:
92
+ f.write(error_msg)
93
+ except Exception:
94
+ pass
95
+ sys.exit(0) # Don't block commit on error
96
+
97
+ sys.exit(0)
98
+
99
+
100
+ if __name__ == "__main__":
101
+ main()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "diffron"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "Git commit message and PR description generator using Lemonade"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -55,10 +55,11 @@ Documentation = "https://github.com/diffron/diffron/docs"
55
55
  Repository = "https://github.com/diffron/diffron"
56
56
 
57
57
  [tool.setuptools.packages.find]
58
- exclude = ["tests", "hooks"]
58
+ exclude = ["tests"]
59
59
 
60
60
  [tool.setuptools.package-data]
61
61
  diffron = ["py.typed"]
62
+ "diffron.hooks" = ["*"]
62
63
 
63
64
  [tool.black]
64
65
  line-length = 100
@@ -1,64 +1,64 @@
1
- """Setup script for Diffron package."""
2
-
3
- from setuptools import setup, find_packages
4
-
5
- with open("README.md", "r", encoding="utf-8") as fh:
6
- long_description = fh.read()
7
-
8
- with open("requirements.txt", "r", encoding="utf-8") as fh:
9
- requirements = [line.strip() for line in fh if line.strip() and not line.startswith("#")]
10
-
11
- setup(
12
- name="diffron",
13
- version="0.1.0",
14
- author="Diffron Contributors",
15
- author_email="diffron@example.com",
16
- description="Git commit message and PR description generator using Lemonade",
17
- long_description=long_description,
18
- long_description_content_type="text/markdown",
19
- url="https://github.com/diffron/diffron",
20
- packages=find_packages(exclude=["tests", "hooks"]),
21
- classifiers=[
22
- "Development Status :: 3 - Alpha",
23
- "Environment :: Console",
24
- "Intended Audience :: Developers",
25
- "License :: OSI Approved :: MIT License",
26
- "Operating System :: Microsoft :: Windows",
27
- "Operating System :: POSIX :: Linux",
28
- "Operating System :: MacOS :: MacOS X",
29
- "Programming Language :: Python :: 3",
30
- "Programming Language :: Python :: 3.9",
31
- "Programming Language :: Python :: 3.10",
32
- "Programming Language :: Python :: 3.11",
33
- "Programming Language :: Python :: 3.12",
34
- "Topic :: Software Development :: Version Control :: Git",
35
- "Topic :: Scientific/Engineering :: Artificial Intelligence",
36
- ],
37
- python_requires=">=3.9",
38
- install_requires=[
39
- "openai>=1.0.0",
40
- "psutil>=5.9.0",
41
- ],
42
- extras_require={
43
- "git": ["gitpython>=3.1.0"],
44
- "dev": [
45
- "pytest>=7.0.0",
46
- "pytest-cov>=4.0.0",
47
- "black>=23.0.0",
48
- "isort>=5.12.0",
49
- "mypy>=1.0.0",
50
- ],
51
- },
52
- entry_points={
53
- "console_scripts": [
54
- "diffron-install-hooks=diffron.cli:install_hooks_cli",
55
- "diffron-uninstall-hooks=diffron.cli:uninstall_hooks_cli",
56
- "diffron-pr=diffron.cli:pr_description_cli",
57
- "diffron-status=diffron.cli:status_cli",
58
- ],
59
- },
60
- include_package_data=True,
61
- package_data={
62
- "diffron": ["py.typed"],
63
- },
64
- )
1
+ """Setup script for Diffron package."""
2
+
3
+ from setuptools import setup, find_packages
4
+
5
+ with open("README.md", "r", encoding="utf-8") as fh:
6
+ long_description = fh.read()
7
+
8
+ with open("requirements.txt", "r", encoding="utf-8") as fh:
9
+ requirements = [line.strip() for line in fh if line.strip() and not line.startswith("#")]
10
+
11
+ setup(
12
+ name="diffron",
13
+ version="0.1.2",
14
+ author="Diffron Contributors",
15
+ author_email="diffron@example.com",
16
+ description="Git commit message and PR description generator using Lemonade",
17
+ long_description=long_description,
18
+ long_description_content_type="text/markdown",
19
+ url="https://github.com/diffron/diffron",
20
+ packages=find_packages(exclude=["tests", "hooks"]),
21
+ classifiers=[
22
+ "Development Status :: 3 - Alpha",
23
+ "Environment :: Console",
24
+ "Intended Audience :: Developers",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Operating System :: Microsoft :: Windows",
27
+ "Operating System :: POSIX :: Linux",
28
+ "Operating System :: MacOS :: MacOS X",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.9",
31
+ "Programming Language :: Python :: 3.10",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ "Topic :: Software Development :: Version Control :: Git",
35
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
36
+ ],
37
+ python_requires=">=3.9",
38
+ install_requires=[
39
+ "openai>=1.0.0",
40
+ "psutil>=5.9.0",
41
+ ],
42
+ extras_require={
43
+ "git": ["gitpython>=3.1.0"],
44
+ "dev": [
45
+ "pytest>=7.0.0",
46
+ "pytest-cov>=4.0.0",
47
+ "black>=23.0.0",
48
+ "isort>=5.12.0",
49
+ "mypy>=1.0.0",
50
+ ],
51
+ },
52
+ entry_points={
53
+ "console_scripts": [
54
+ "diffron-install-hooks=diffron.cli:install_hooks_cli",
55
+ "diffron-uninstall-hooks=diffron.cli:uninstall_hooks_cli",
56
+ "diffron-pr=diffron.cli:pr_description_cli",
57
+ "diffron-status=diffron.cli:status_cli",
58
+ ],
59
+ },
60
+ include_package_data=True,
61
+ package_data={
62
+ "diffron": ["py.typed"],
63
+ },
64
+ )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes