diffron 0.1.0__py3-none-any.whl
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.
- diffron/__init__.py +26 -0
- diffron/__init__.pyi +28 -0
- diffron/cli.py +347 -0
- diffron/cli.pyi +30 -0
- diffron/client.py +175 -0
- diffron/client.pyi +80 -0
- diffron/commit_gen.py +151 -0
- diffron/commit_gen.pyi +41 -0
- diffron/git_hooks.py +292 -0
- diffron/git_hooks.pyi +52 -0
- diffron/lemonade.py +192 -0
- diffron/lemonade.pyi +66 -0
- diffron/pr_gen.py +224 -0
- diffron/pr_gen.pyi +56 -0
- diffron/py.typed +0 -0
- diffron/utils.py +236 -0
- diffron/utils.pyi +58 -0
- diffron-0.1.0.dist-info/METADATA +373 -0
- diffron-0.1.0.dist-info/RECORD +28 -0
- diffron-0.1.0.dist-info/WHEEL +5 -0
- diffron-0.1.0.dist-info/entry_points.txt +5 -0
- diffron-0.1.0.dist-info/licenses/LICENSE +21 -0
- diffron-0.1.0.dist-info/top_level.txt +2 -0
- docs/HOOKS.md +442 -0
- docs/PLAN.md +378 -0
- docs/PYPI_RELEASE.md +487 -0
- docs/SETUP.md +524 -0
- docs/USAGE.md +515 -0
diffron/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Diffron - AI-powered Git automation with Lemonade.
|
|
3
|
+
|
|
4
|
+
Automatically generates commit messages and PR descriptions using
|
|
5
|
+
your local Lemonade LLM server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .client import DiffronClient
|
|
9
|
+
from .lemonade import LemonadeClient, detect_lemonade_port
|
|
10
|
+
from .commit_gen import generate_commit_message
|
|
11
|
+
from .pr_gen import generate_pr_description, PRDescription
|
|
12
|
+
from .git_hooks import install_hooks, uninstall_hooks, is_hooks_installed
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
__author__ = "Diffron Contributors"
|
|
16
|
+
__all__ = [
|
|
17
|
+
"DiffronClient",
|
|
18
|
+
"LemonadeClient",
|
|
19
|
+
"detect_lemonade_port",
|
|
20
|
+
"generate_commit_message",
|
|
21
|
+
"generate_pr_description",
|
|
22
|
+
"PRDescription",
|
|
23
|
+
"install_hooks",
|
|
24
|
+
"uninstall_hooks",
|
|
25
|
+
"is_hooks_installed",
|
|
26
|
+
]
|
diffron/__init__.pyi
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Diffron - AI-powered Git automation with Lemonade.
|
|
3
|
+
|
|
4
|
+
Automatically generates commit messages and PR descriptions using
|
|
5
|
+
your local Lemonade LLM server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from .client import DiffronClient
|
|
10
|
+
from .lemonade import LemonadeClient
|
|
11
|
+
from .commit_gen import generate_commit_message
|
|
12
|
+
from .pr_gen import generate_pr_description, PRDescription
|
|
13
|
+
from .git_hooks import install_hooks, uninstall_hooks, is_hooks_installed
|
|
14
|
+
|
|
15
|
+
__version__: str
|
|
16
|
+
__author__: str
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"DiffronClient",
|
|
20
|
+
"LemonadeClient",
|
|
21
|
+
"detect_lemonade_port",
|
|
22
|
+
"generate_commit_message",
|
|
23
|
+
"generate_pr_description",
|
|
24
|
+
"PRDescription",
|
|
25
|
+
"install_hooks",
|
|
26
|
+
"uninstall_hooks",
|
|
27
|
+
"is_hooks_installed",
|
|
28
|
+
]
|
diffron/cli.py
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for Diffron.
|
|
3
|
+
|
|
4
|
+
Provides CLI entry points for installing hooks, generating PRs, and checking status.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def install_hooks_cli():
|
|
13
|
+
"""CLI entry point for installing Git hooks."""
|
|
14
|
+
parser = argparse.ArgumentParser(
|
|
15
|
+
description="Install Diffron Git hooks to a repository."
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"--global",
|
|
19
|
+
dest="global_install",
|
|
20
|
+
action="store_true",
|
|
21
|
+
help="Install hooks globally for all repositories.",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--repo",
|
|
25
|
+
"-r",
|
|
26
|
+
type=str,
|
|
27
|
+
default=".",
|
|
28
|
+
help="Path to the git repository (default: current directory).",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
args = parser.parse_args()
|
|
32
|
+
|
|
33
|
+
from .git_hooks import install_hooks
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
success = install_hooks(
|
|
37
|
+
repo_path=args.repo,
|
|
38
|
+
global_install=args.global_install,
|
|
39
|
+
)
|
|
40
|
+
if success:
|
|
41
|
+
if args.global_install:
|
|
42
|
+
print("Diffron hooks installed globally for all repositories.")
|
|
43
|
+
else:
|
|
44
|
+
print(f"Diffron hooks installed to: {args.repo}")
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
else:
|
|
47
|
+
print("Failed to install hooks.", file=sys.stderr)
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
except ValueError as e:
|
|
50
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print(f"Unexpected error: {e}", file=sys.stderr)
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def uninstall_hooks_cli():
|
|
58
|
+
"""CLI entry point for uninstalling Git hooks."""
|
|
59
|
+
parser = argparse.ArgumentParser(
|
|
60
|
+
description="Remove Diffron Git hooks from a repository."
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--global",
|
|
64
|
+
dest="global_install",
|
|
65
|
+
action="store_true",
|
|
66
|
+
help="Remove global hooks configuration.",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--repo",
|
|
70
|
+
"-r",
|
|
71
|
+
type=str,
|
|
72
|
+
default=".",
|
|
73
|
+
help="Path to the git repository (default: current directory).",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
args = parser.parse_args()
|
|
77
|
+
|
|
78
|
+
from .git_hooks import uninstall_hooks
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
success = uninstall_hooks(
|
|
82
|
+
repo_path=args.repo,
|
|
83
|
+
global_install=args.global_install,
|
|
84
|
+
)
|
|
85
|
+
if success:
|
|
86
|
+
if args.global_install:
|
|
87
|
+
print("Diffron global hooks configuration removed.")
|
|
88
|
+
else:
|
|
89
|
+
print(f"Diffron hooks removed from: {args.repo}")
|
|
90
|
+
sys.exit(0)
|
|
91
|
+
else:
|
|
92
|
+
print("No hooks found to remove.", file=sys.stderr)
|
|
93
|
+
sys.exit(1)
|
|
94
|
+
except ValueError as e:
|
|
95
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
96
|
+
sys.exit(1)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
print(f"Unexpected error: {e}", file=sys.stderr)
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def pr_description_cli():
|
|
103
|
+
"""CLI entry point for generating PR descriptions."""
|
|
104
|
+
parser = argparse.ArgumentParser(
|
|
105
|
+
description="Generate GitHub PR title and description."
|
|
106
|
+
)
|
|
107
|
+
parser.add_argument(
|
|
108
|
+
"--branch",
|
|
109
|
+
"-b",
|
|
110
|
+
type=str,
|
|
111
|
+
default=None,
|
|
112
|
+
help="Branch to analyze (default: current branch).",
|
|
113
|
+
)
|
|
114
|
+
parser.add_argument(
|
|
115
|
+
"--base",
|
|
116
|
+
type=str,
|
|
117
|
+
default=None,
|
|
118
|
+
help="Base branch to compare against (default: main/master).",
|
|
119
|
+
)
|
|
120
|
+
parser.add_argument(
|
|
121
|
+
"--create",
|
|
122
|
+
"-c",
|
|
123
|
+
action="store_true",
|
|
124
|
+
help="Automatically create PR on GitHub (requires gh CLI).",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
args = parser.parse_args()
|
|
128
|
+
|
|
129
|
+
from .pr_gen import generate_pr_description, create_github_pr
|
|
130
|
+
from .lemonade import is_lemonade_running
|
|
131
|
+
|
|
132
|
+
if not is_lemonade_running():
|
|
133
|
+
print(
|
|
134
|
+
"Error: Lemonade server is not running.",
|
|
135
|
+
file=sys.stderr
|
|
136
|
+
)
|
|
137
|
+
print(
|
|
138
|
+
"Please start Lemonade first (e.g., 'lemonade serve').",
|
|
139
|
+
file=sys.stderr
|
|
140
|
+
)
|
|
141
|
+
sys.exit(1)
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
if args.create:
|
|
145
|
+
pr = create_github_pr(
|
|
146
|
+
branch=args.branch,
|
|
147
|
+
base=args.base,
|
|
148
|
+
auto_submit=True,
|
|
149
|
+
)
|
|
150
|
+
print("PR created successfully!")
|
|
151
|
+
else:
|
|
152
|
+
pr = generate_pr_description(
|
|
153
|
+
branch=args.branch,
|
|
154
|
+
base=args.base,
|
|
155
|
+
)
|
|
156
|
+
print(pr.format_output())
|
|
157
|
+
sys.exit(0)
|
|
158
|
+
except ValueError as e:
|
|
159
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
160
|
+
sys.exit(1)
|
|
161
|
+
except ConnectionError as e:
|
|
162
|
+
print(f"Connection error: {e}", file=sys.stderr)
|
|
163
|
+
sys.exit(1)
|
|
164
|
+
except RuntimeError as e:
|
|
165
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
166
|
+
sys.exit(1)
|
|
167
|
+
except KeyboardInterrupt:
|
|
168
|
+
print("\nCancelled.", file=sys.stderr)
|
|
169
|
+
sys.exit(130)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def status_cli():
|
|
173
|
+
"""CLI entry point for checking Lemonade and hooks status."""
|
|
174
|
+
parser = argparse.ArgumentParser(
|
|
175
|
+
description="Check Diffron status (Lemonade connection, hooks installation)."
|
|
176
|
+
)
|
|
177
|
+
parser.add_argument(
|
|
178
|
+
"--repo",
|
|
179
|
+
"-r",
|
|
180
|
+
type=str,
|
|
181
|
+
default=".",
|
|
182
|
+
help="Path to the git repository (default: current directory).",
|
|
183
|
+
)
|
|
184
|
+
parser.add_argument(
|
|
185
|
+
"--verbose",
|
|
186
|
+
"-v",
|
|
187
|
+
action="store_true",
|
|
188
|
+
help="Show detailed status information.",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
args = parser.parse_args()
|
|
192
|
+
|
|
193
|
+
from .lemonade import detect_lemonade_port, is_lemonade_running
|
|
194
|
+
from .git_hooks import get_hooks_status, is_hooks_installed
|
|
195
|
+
|
|
196
|
+
# Check Lemonade status
|
|
197
|
+
print("Lemonade Server:")
|
|
198
|
+
if is_lemonade_running():
|
|
199
|
+
port = detect_lemonade_port()
|
|
200
|
+
print(f" ✓ Running on port {port}")
|
|
201
|
+
else:
|
|
202
|
+
print(" ✗ Not running")
|
|
203
|
+
print(" Start with: lemonade serve")
|
|
204
|
+
|
|
205
|
+
# Check hooks status
|
|
206
|
+
print("\nGit Hooks:")
|
|
207
|
+
|
|
208
|
+
if args.verbose:
|
|
209
|
+
status = get_hooks_status(args.repo)
|
|
210
|
+
print(f" Is git repo: {status['is_git_repo']}")
|
|
211
|
+
if status['git_dir']:
|
|
212
|
+
print(f" Git directory: {status['git_dir']}")
|
|
213
|
+
if status['hooks_dir']:
|
|
214
|
+
print(f" Hooks directory: {status['hooks_dir']}")
|
|
215
|
+
print(f" Wrapper exists: {status['wrapper_exists']}")
|
|
216
|
+
print(f" Python hook exists: {status['python_hook_exists']}")
|
|
217
|
+
print(f" Local hooks installed: {status['local_hooks_installed']}")
|
|
218
|
+
print(f" Global hooks configured: {status['global_hooks_configured']}")
|
|
219
|
+
else:
|
|
220
|
+
local_installed = is_hooks_installed(args.repo, check_global=False)
|
|
221
|
+
global_configured = is_hooks_installed(args.repo, check_global=True)
|
|
222
|
+
|
|
223
|
+
if local_installed:
|
|
224
|
+
print(f" ✓ Installed locally ({args.repo})")
|
|
225
|
+
elif global_configured:
|
|
226
|
+
print(" ✓ Installed globally")
|
|
227
|
+
else:
|
|
228
|
+
print(" ✗ Not installed")
|
|
229
|
+
print(" Install with: diffron-install-hooks")
|
|
230
|
+
|
|
231
|
+
sys.exit(0)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def main():
|
|
235
|
+
"""Main CLI entry point."""
|
|
236
|
+
parser = argparse.ArgumentParser(
|
|
237
|
+
description="Diffron - AI-powered Git automation with Lemonade",
|
|
238
|
+
prog="diffron",
|
|
239
|
+
)
|
|
240
|
+
parser.add_argument(
|
|
241
|
+
"--version",
|
|
242
|
+
"-V",
|
|
243
|
+
action="version",
|
|
244
|
+
version="%(prog)s 0.1.0",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
248
|
+
|
|
249
|
+
# Install hooks command
|
|
250
|
+
install_parser = subparsers.add_parser(
|
|
251
|
+
"install-hooks",
|
|
252
|
+
help="Install Git hooks to a repository",
|
|
253
|
+
)
|
|
254
|
+
install_parser.add_argument(
|
|
255
|
+
"--global",
|
|
256
|
+
dest="global_install",
|
|
257
|
+
action="store_true",
|
|
258
|
+
help="Install hooks globally for all repositories.",
|
|
259
|
+
)
|
|
260
|
+
install_parser.add_argument(
|
|
261
|
+
"--repo",
|
|
262
|
+
"-r",
|
|
263
|
+
type=str,
|
|
264
|
+
default=".",
|
|
265
|
+
help="Path to the git repository (default: current directory).",
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Uninstall hooks command
|
|
269
|
+
uninstall_parser = subparsers.add_parser(
|
|
270
|
+
"uninstall-hooks",
|
|
271
|
+
help="Remove Git hooks from a repository",
|
|
272
|
+
)
|
|
273
|
+
uninstall_parser.add_argument(
|
|
274
|
+
"--global",
|
|
275
|
+
dest="global_install",
|
|
276
|
+
action="store_true",
|
|
277
|
+
help="Remove global hooks configuration.",
|
|
278
|
+
)
|
|
279
|
+
uninstall_parser.add_argument(
|
|
280
|
+
"--repo",
|
|
281
|
+
"-r",
|
|
282
|
+
type=str,
|
|
283
|
+
default=".",
|
|
284
|
+
help="Path to the git repository (default: current directory).",
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# PR command
|
|
288
|
+
pr_parser = subparsers.add_parser(
|
|
289
|
+
"pr",
|
|
290
|
+
help="Generate PR title and description",
|
|
291
|
+
)
|
|
292
|
+
pr_parser.add_argument(
|
|
293
|
+
"--branch",
|
|
294
|
+
"-b",
|
|
295
|
+
type=str,
|
|
296
|
+
default=None,
|
|
297
|
+
help="Branch to analyze (default: current branch).",
|
|
298
|
+
)
|
|
299
|
+
pr_parser.add_argument(
|
|
300
|
+
"--base",
|
|
301
|
+
type=str,
|
|
302
|
+
default=None,
|
|
303
|
+
help="Base branch to compare against (default: main/master).",
|
|
304
|
+
)
|
|
305
|
+
pr_parser.add_argument(
|
|
306
|
+
"--create",
|
|
307
|
+
"-c",
|
|
308
|
+
action="store_true",
|
|
309
|
+
help="Automatically create PR on GitHub (requires gh CLI).",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Status command
|
|
313
|
+
status_parser = subparsers.add_parser(
|
|
314
|
+
"status",
|
|
315
|
+
help="Check Diffron status",
|
|
316
|
+
)
|
|
317
|
+
status_parser.add_argument(
|
|
318
|
+
"--repo",
|
|
319
|
+
"-r",
|
|
320
|
+
type=str,
|
|
321
|
+
default=".",
|
|
322
|
+
help="Path to the git repository (default: current directory).",
|
|
323
|
+
)
|
|
324
|
+
status_parser.add_argument(
|
|
325
|
+
"--verbose",
|
|
326
|
+
"-v",
|
|
327
|
+
action="store_true",
|
|
328
|
+
help="Show detailed status information.",
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
args = parser.parse_args()
|
|
332
|
+
|
|
333
|
+
if args.command == "install-hooks":
|
|
334
|
+
install_hooks_cli()
|
|
335
|
+
elif args.command == "uninstall-hooks":
|
|
336
|
+
uninstall_hooks_cli()
|
|
337
|
+
elif args.command == "pr":
|
|
338
|
+
pr_description_cli()
|
|
339
|
+
elif args.command == "status":
|
|
340
|
+
status_cli()
|
|
341
|
+
else:
|
|
342
|
+
parser.print_help()
|
|
343
|
+
sys.exit(0)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
if __name__ == "__main__":
|
|
347
|
+
main()
|
diffron/cli.pyi
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for Diffron.
|
|
3
|
+
|
|
4
|
+
Provides CLI entry points for installing hooks, generating PRs, and checking status.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def install_hooks_cli():
|
|
9
|
+
"""CLI entry point for installing Git hooks."""
|
|
10
|
+
...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def uninstall_hooks_cli():
|
|
14
|
+
"""CLI entry point for uninstalling Git hooks."""
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def pr_description_cli():
|
|
19
|
+
"""CLI entry point for generating PR descriptions."""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def status_cli():
|
|
24
|
+
"""CLI entry point for checking Lemonade and hooks status."""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main():
|
|
29
|
+
"""Main CLI entry point."""
|
|
30
|
+
...
|
diffron/client.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main Diffron client - unified interface for all Diffron functionality.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from .lemonade import LemonadeClient, detect_lemonade_port
|
|
8
|
+
from .commit_gen import generate_commit_message
|
|
9
|
+
from .pr_gen import generate_pr_description, PRDescription
|
|
10
|
+
from .git_hooks import install_hooks, uninstall_hooks, is_hooks_installed
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DiffronClient:
|
|
14
|
+
"""
|
|
15
|
+
Unified client for Diffron Git automation.
|
|
16
|
+
|
|
17
|
+
Provides a single interface for commit message generation,
|
|
18
|
+
PR description generation, and Git hooks management.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
model: Optional[str] = None,
|
|
24
|
+
host: Optional[str] = None,
|
|
25
|
+
port: Optional[int] = None,
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Initialize Diffron client.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
model: Model name to use. Auto-detects if not provided.
|
|
32
|
+
host: Lemonade server host. Auto-detects if not provided.
|
|
33
|
+
port: Lemonade server port. Auto-detects if not provided.
|
|
34
|
+
"""
|
|
35
|
+
self._lemonade_client: Optional[LemonadeClient] = None
|
|
36
|
+
self._model = model
|
|
37
|
+
self._host = host
|
|
38
|
+
self._port = port
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def lemonade_client(self) -> LemonadeClient:
|
|
42
|
+
"""Get or create Lemonade client."""
|
|
43
|
+
if self._lemonade_client is None:
|
|
44
|
+
self._lemonade_client = LemonadeClient(
|
|
45
|
+
host=self._host,
|
|
46
|
+
port=self._port,
|
|
47
|
+
model=self._model,
|
|
48
|
+
)
|
|
49
|
+
return self._lemonade_client
|
|
50
|
+
|
|
51
|
+
def detect_lemonade_port(self) -> Optional[int]:
|
|
52
|
+
"""
|
|
53
|
+
Detect the port where Lemonade is running.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Port number if found, None otherwise.
|
|
57
|
+
"""
|
|
58
|
+
return detect_lemonade_port(host=self._host)
|
|
59
|
+
|
|
60
|
+
def generate_commit_message(
|
|
61
|
+
self,
|
|
62
|
+
diff: Optional[str] = None,
|
|
63
|
+
max_chars: Optional[int] = None,
|
|
64
|
+
max_tokens: Optional[int] = None,
|
|
65
|
+
temperature: Optional[float] = None,
|
|
66
|
+
) -> str:
|
|
67
|
+
"""
|
|
68
|
+
Generate a commit message from a git diff.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
diff: Git diff string. If None, gets staged diff automatically.
|
|
72
|
+
max_chars: Maximum characters of diff to send.
|
|
73
|
+
max_tokens: Maximum tokens to generate.
|
|
74
|
+
temperature: Sampling temperature.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Generated commit message in Conventional Commits format.
|
|
78
|
+
"""
|
|
79
|
+
return generate_commit_message(
|
|
80
|
+
diff=diff,
|
|
81
|
+
max_chars=max_chars,
|
|
82
|
+
max_tokens=max_tokens,
|
|
83
|
+
temperature=temperature,
|
|
84
|
+
client=self.lemonade_client,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def generate_pr_description(
|
|
88
|
+
self,
|
|
89
|
+
branch: Optional[str] = None,
|
|
90
|
+
base: Optional[str] = None,
|
|
91
|
+
max_chars: Optional[int] = None,
|
|
92
|
+
max_tokens: Optional[int] = None,
|
|
93
|
+
temperature: Optional[float] = None,
|
|
94
|
+
) -> PRDescription:
|
|
95
|
+
"""
|
|
96
|
+
Generate a PR title and description from branch changes.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
branch: Branch name to analyze. Defaults to current branch.
|
|
100
|
+
base: Base branch to compare against. Defaults to main/master.
|
|
101
|
+
max_chars: Maximum characters of diff to send.
|
|
102
|
+
max_tokens: Maximum tokens to generate.
|
|
103
|
+
temperature: Sampling temperature.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
PRDescription with generated title and description.
|
|
107
|
+
"""
|
|
108
|
+
return generate_pr_description(
|
|
109
|
+
branch=branch,
|
|
110
|
+
base=base,
|
|
111
|
+
max_chars=max_chars,
|
|
112
|
+
max_tokens=max_tokens,
|
|
113
|
+
temperature=temperature,
|
|
114
|
+
client=self.lemonade_client,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def install_hooks(
|
|
118
|
+
self,
|
|
119
|
+
repo_path: str = ".",
|
|
120
|
+
global_install: bool = False,
|
|
121
|
+
) -> bool:
|
|
122
|
+
"""
|
|
123
|
+
Install Diffron Git hooks.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
repo_path: Path to the git repository.
|
|
127
|
+
global_install: If True, install globally for all repositories.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
True if installation was successful.
|
|
131
|
+
"""
|
|
132
|
+
return install_hooks(
|
|
133
|
+
repo_path=repo_path,
|
|
134
|
+
global_install=global_install,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def uninstall_hooks(
|
|
138
|
+
self,
|
|
139
|
+
repo_path: str = ".",
|
|
140
|
+
global_install: bool = False,
|
|
141
|
+
) -> bool:
|
|
142
|
+
"""
|
|
143
|
+
Remove Diffron Git hooks.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
repo_path: Path to the git repository.
|
|
147
|
+
global_install: If True, remove global hooks configuration.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
True if uninstallation was successful.
|
|
151
|
+
"""
|
|
152
|
+
return uninstall_hooks(
|
|
153
|
+
repo_path=repo_path,
|
|
154
|
+
global_install=global_install,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def is_hooks_installed(
|
|
158
|
+
self,
|
|
159
|
+
repo_path: str = ".",
|
|
160
|
+
check_global: bool = False,
|
|
161
|
+
) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
Check if Diffron hooks are installed.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
repo_path: Path to the git repository.
|
|
167
|
+
check_global: If True, check for global hooks configuration.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
True if hooks are installed.
|
|
171
|
+
"""
|
|
172
|
+
return is_hooks_installed(
|
|
173
|
+
repo_path=repo_path,
|
|
174
|
+
check_global=check_global,
|
|
175
|
+
)
|
diffron/client.pyi
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main Diffron client - unified interface for all Diffron functionality.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from .lemonade import LemonadeClient
|
|
7
|
+
from .commit_gen import generate_commit_message
|
|
8
|
+
from .pr_gen import generate_pr_description, PRDescription
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DiffronClient:
|
|
12
|
+
"""Unified client for Diffron Git automation."""
|
|
13
|
+
|
|
14
|
+
_lemonade_client: Optional[LemonadeClient]
|
|
15
|
+
_model: Optional[str]
|
|
16
|
+
_host: Optional[str]
|
|
17
|
+
_port: Optional[int]
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
model: Optional[str] = None,
|
|
22
|
+
host: Optional[str] = None,
|
|
23
|
+
port: Optional[int] = None,
|
|
24
|
+
):
|
|
25
|
+
"""Initialize Diffron client."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def lemonade_client(self) -> LemonadeClient:
|
|
30
|
+
"""Get or create Lemonade client."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
def detect_lemonade_port(self) -> Optional[int]:
|
|
34
|
+
"""Detect the port where Lemonade is running."""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
def generate_commit_message(
|
|
38
|
+
self,
|
|
39
|
+
diff: Optional[str] = None,
|
|
40
|
+
max_chars: Optional[int] = None,
|
|
41
|
+
max_tokens: Optional[int] = None,
|
|
42
|
+
temperature: Optional[float] = None,
|
|
43
|
+
) -> str:
|
|
44
|
+
"""Generate a commit message from a git diff."""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
def generate_pr_description(
|
|
48
|
+
self,
|
|
49
|
+
branch: Optional[str] = None,
|
|
50
|
+
base: Optional[str] = None,
|
|
51
|
+
max_chars: Optional[int] = None,
|
|
52
|
+
max_tokens: Optional[int] = None,
|
|
53
|
+
temperature: Optional[float] = None,
|
|
54
|
+
) -> PRDescription:
|
|
55
|
+
"""Generate a PR title and description from branch changes."""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
def install_hooks(
|
|
59
|
+
self,
|
|
60
|
+
repo_path: str = ".",
|
|
61
|
+
global_install: bool = False,
|
|
62
|
+
) -> bool:
|
|
63
|
+
"""Install Diffron Git hooks."""
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
def uninstall_hooks(
|
|
67
|
+
self,
|
|
68
|
+
repo_path: str = ".",
|
|
69
|
+
global_install: bool = False,
|
|
70
|
+
) -> bool:
|
|
71
|
+
"""Remove Diffron Git hooks."""
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
def is_hooks_installed(
|
|
75
|
+
self,
|
|
76
|
+
repo_path: str = ".",
|
|
77
|
+
check_global: bool = False,
|
|
78
|
+
) -> bool:
|
|
79
|
+
"""Check if Diffron hooks are installed."""
|
|
80
|
+
...
|