git-copilot-commit 0.6.0__py3-none-any.whl → 0.7.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.
- git_copilot_commit/cli.py +194 -130
- git_copilot_commit/git.py +123 -0
- git_copilot_commit/llms/copilot.py +15 -1
- git_copilot_commit/llms/core.py +194 -16
- git_copilot_commit/llms/openai_api.py +67 -7
- git_copilot_commit/llms/providers.py +10 -9
- {git_copilot_commit-0.6.0.dist-info → git_copilot_commit-0.7.0.dist-info}/METADATA +104 -35
- git_copilot_commit-0.7.0.dist-info/RECORD +19 -0
- git_copilot_commit-0.6.0.dist-info/RECORD +0 -19
- {git_copilot_commit-0.6.0.dist-info → git_copilot_commit-0.7.0.dist-info}/WHEEL +0 -0
- {git_copilot_commit-0.6.0.dist-info → git_copilot_commit-0.7.0.dist-info}/entry_points.txt +0 -0
- {git_copilot_commit-0.6.0.dist-info → git_copilot_commit-0.7.0.dist-info}/licenses/LICENSE +0 -0
git_copilot_commit/cli.py
CHANGED
|
@@ -9,12 +9,10 @@ import re
|
|
|
9
9
|
import sys
|
|
10
10
|
from typing import Annotated, Sequence
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
import typer
|
|
12
|
+
import cyclopts
|
|
14
13
|
from rich.console import Console
|
|
15
14
|
from rich.panel import Panel
|
|
16
|
-
from rich.prompt import Confirm
|
|
17
|
-
from typer.main import get_command
|
|
15
|
+
from rich.prompt import Confirm, Prompt
|
|
18
16
|
|
|
19
17
|
from .git import GitRepository, GitError, GitStatus, NotAGitRepositoryError
|
|
20
18
|
from .split_commits import (
|
|
@@ -34,7 +32,7 @@ from .llms import core as llm
|
|
|
34
32
|
from .llms import providers
|
|
35
33
|
|
|
36
34
|
console = Console()
|
|
37
|
-
app =
|
|
35
|
+
app = cyclopts.App(help=__doc__, version=lambda: f"git-copilot-commit {__version__}")
|
|
38
36
|
|
|
39
37
|
COMMIT_MESSAGE_PROMPT_FILENAME = "commit-message-generator-prompt.md"
|
|
40
38
|
SPLIT_COMMIT_PLANNER_PROMPT_FILENAME = "split-commit-planner-prompt.md"
|
|
@@ -57,38 +55,41 @@ NATIVE_TLS_HELP = (
|
|
|
57
55
|
|
|
58
56
|
CaBundleOption = Annotated[
|
|
59
57
|
str | None,
|
|
60
|
-
|
|
58
|
+
cyclopts.Parameter(name="--ca-bundle", help=CA_BUNDLE_HELP),
|
|
61
59
|
]
|
|
62
60
|
InsecureOption = Annotated[
|
|
63
61
|
bool,
|
|
64
|
-
|
|
62
|
+
cyclopts.Parameter(name="--insecure", help="Disable SSL certificate verification."),
|
|
65
63
|
]
|
|
66
64
|
NativeTlsOption = Annotated[
|
|
67
65
|
bool,
|
|
68
|
-
|
|
66
|
+
cyclopts.Parameter(
|
|
67
|
+
name="--native-tls",
|
|
68
|
+
negative="--no-native-tls",
|
|
69
|
+
help=NATIVE_TLS_HELP,
|
|
70
|
+
),
|
|
69
71
|
]
|
|
70
72
|
ProviderOption = Annotated[
|
|
71
73
|
str | None,
|
|
72
|
-
|
|
73
|
-
"--provider",
|
|
74
|
+
cyclopts.Parameter(
|
|
75
|
+
name="--provider",
|
|
74
76
|
help="LLM provider to use: copilot or openai.",
|
|
75
77
|
),
|
|
76
78
|
]
|
|
77
79
|
BaseUrlOption = Annotated[
|
|
78
80
|
str | None,
|
|
79
|
-
|
|
80
|
-
"--base-url",
|
|
81
|
-
metavar="URL",
|
|
81
|
+
cyclopts.Parameter(
|
|
82
|
+
name="--base-url",
|
|
82
83
|
help=(
|
|
83
|
-
"
|
|
84
|
-
"http://127.0.0.1:11434/v1."
|
|
84
|
+
"Endpoint URL for an OpenAI-compatible provider, for example "
|
|
85
|
+
"http://127.0.0.1:11434/v1/chat/completions."
|
|
85
86
|
),
|
|
86
87
|
),
|
|
87
88
|
]
|
|
88
89
|
ApiKeyOption = Annotated[
|
|
89
90
|
str | None,
|
|
90
|
-
|
|
91
|
-
"--api-key",
|
|
91
|
+
cyclopts.Parameter(
|
|
92
|
+
name="--api-key",
|
|
92
93
|
help="API key for an OpenAI-compatible provider. Omit when the server does not require one.",
|
|
93
94
|
),
|
|
94
95
|
]
|
|
@@ -96,8 +97,8 @@ ApiKeyOption = Annotated[
|
|
|
96
97
|
|
|
97
98
|
SplitOption = Annotated[
|
|
98
99
|
bool,
|
|
99
|
-
|
|
100
|
-
"--split",
|
|
100
|
+
cyclopts.Parameter(
|
|
101
|
+
name="--split",
|
|
101
102
|
help=(
|
|
102
103
|
"Split staged hunks into multiple commits automatically. Pass "
|
|
103
104
|
"`--split=N` to express a preference for N commits."
|
|
@@ -106,10 +107,10 @@ SplitOption = Annotated[
|
|
|
106
107
|
]
|
|
107
108
|
SplitCountOption = Annotated[
|
|
108
109
|
int | None,
|
|
109
|
-
|
|
110
|
-
"--split-count",
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
cyclopts.Parameter(
|
|
111
|
+
name="--split-count",
|
|
112
|
+
show=False,
|
|
113
|
+
validator=cyclopts.validators.Number(gte=1),
|
|
113
114
|
),
|
|
114
115
|
]
|
|
115
116
|
|
|
@@ -225,39 +226,13 @@ def order_prepared_split_commits(
|
|
|
225
226
|
def run(args: Sequence[str] | None = None) -> None:
|
|
226
227
|
"""Run the CLI entrypoint with argument normalization."""
|
|
227
228
|
raw_args = list(args) if args is not None else sys.argv[1:]
|
|
228
|
-
|
|
229
|
-
command.main(
|
|
230
|
-
args=preprocess_cli_args(raw_args),
|
|
231
|
-
prog_name=Path(sys.argv[0]).name,
|
|
232
|
-
)
|
|
229
|
+
app(preprocess_cli_args(raw_args))
|
|
233
230
|
|
|
234
231
|
|
|
235
|
-
def
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
@app.callback(invoke_without_command=True)
|
|
242
|
-
def main(
|
|
243
|
-
ctx: typer.Context,
|
|
244
|
-
_: bool = typer.Option(
|
|
245
|
-
False, "--version", callback=version_callback, help="Show version and exit"
|
|
246
|
-
),
|
|
247
|
-
):
|
|
248
|
-
"""
|
|
249
|
-
Automatically commit changes in the current git repository.
|
|
250
|
-
"""
|
|
251
|
-
if ctx.invoked_subcommand is None:
|
|
252
|
-
# Show help when no command is provided
|
|
253
|
-
console.print(ctx.get_help())
|
|
254
|
-
raise typer.Exit()
|
|
255
|
-
else:
|
|
256
|
-
# Don't show version for print command to avoid interfering with pipes
|
|
257
|
-
if ctx.invoked_subcommand != "echo":
|
|
258
|
-
console.print(
|
|
259
|
-
f"[bold]{(__package__ or 'git_copilot_commit').replace('_', '-')}[/] - [bold green]v{__version__}[/]\n"
|
|
260
|
-
)
|
|
232
|
+
def print_cli_banner() -> None:
|
|
233
|
+
"""Render the CLI banner before command output."""
|
|
234
|
+
package_name = (__package__ or "git_copilot_commit").replace("_", "-")
|
|
235
|
+
console.print(f"[bold]{package_name}[/] - [bold green]v{__version__}[/]\n")
|
|
261
236
|
|
|
262
237
|
|
|
263
238
|
def get_prompt_locations(filename: str):
|
|
@@ -280,7 +255,7 @@ def resolve_prompt_file() -> Path | None:
|
|
|
280
255
|
console.print(
|
|
281
256
|
f"[red]Configured default prompt file in {settings.config_file} is invalid.[/red]"
|
|
282
257
|
)
|
|
283
|
-
raise
|
|
258
|
+
raise SystemExit(1)
|
|
284
259
|
|
|
285
260
|
if configured_prompt_file is None:
|
|
286
261
|
return None
|
|
@@ -298,7 +273,7 @@ def load_system_prompt() -> str:
|
|
|
298
273
|
console.print(
|
|
299
274
|
f"[red]Error reading prompt file {resolved_prompt_file}: {exc}[/red]"
|
|
300
275
|
)
|
|
301
|
-
raise
|
|
276
|
+
raise SystemExit(1)
|
|
302
277
|
|
|
303
278
|
return load_named_prompt(COMMIT_MESSAGE_PROMPT_FILENAME)
|
|
304
279
|
|
|
@@ -312,7 +287,7 @@ def load_named_prompt(filename: str) -> str:
|
|
|
312
287
|
continue
|
|
313
288
|
|
|
314
289
|
console.print(f"[red]Error: Prompt file {filename} not found in any location[/red]")
|
|
315
|
-
raise
|
|
290
|
+
raise SystemExit(1)
|
|
316
291
|
|
|
317
292
|
|
|
318
293
|
def build_http_client_config(
|
|
@@ -357,7 +332,7 @@ def build_commit_message_prompt(
|
|
|
357
332
|
"""Build the prompt used to generate a commit message."""
|
|
358
333
|
if not status.has_staged_changes:
|
|
359
334
|
console.print("[red]No staged changes to commit.[/red]")
|
|
360
|
-
raise
|
|
335
|
+
raise SystemExit()
|
|
361
336
|
|
|
362
337
|
prompt_parts = [
|
|
363
338
|
"`git status`:\n",
|
|
@@ -383,7 +358,6 @@ def normalize_model_name(model: str | None) -> str | None:
|
|
|
383
358
|
if model is not None:
|
|
384
359
|
for prefix in (
|
|
385
360
|
"copilot/",
|
|
386
|
-
"openai/",
|
|
387
361
|
"openai-compatible/",
|
|
388
362
|
):
|
|
389
363
|
if model.startswith(prefix):
|
|
@@ -397,6 +371,8 @@ def ask_llm_with_system_prompt(
|
|
|
397
371
|
model: str | None = None,
|
|
398
372
|
provider_config: providers.ProviderConfig | None = None,
|
|
399
373
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
374
|
+
disable_thinking: bool = True,
|
|
375
|
+
max_tokens: int = 1024,
|
|
400
376
|
) -> str:
|
|
401
377
|
"""Send a prepared prompt to the selected LLM provider."""
|
|
402
378
|
return providers.ask(
|
|
@@ -412,6 +388,8 @@ def ask_llm_with_system_prompt(
|
|
|
412
388
|
provider_config=provider_config,
|
|
413
389
|
model=normalize_model_name(model),
|
|
414
390
|
http_client_config=http_client_config,
|
|
391
|
+
disable_thinking=disable_thinking,
|
|
392
|
+
max_tokens=max_tokens,
|
|
415
393
|
)
|
|
416
394
|
|
|
417
395
|
|
|
@@ -420,6 +398,8 @@ def generate_commit_message_for_prompt(
|
|
|
420
398
|
model: str | None = None,
|
|
421
399
|
provider_config: providers.ProviderConfig | None = None,
|
|
422
400
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
401
|
+
disable_thinking: bool = True,
|
|
402
|
+
max_tokens: int = 1024,
|
|
423
403
|
) -> str:
|
|
424
404
|
"""Generate a conventional commit message from a prepared prompt."""
|
|
425
405
|
return ask_llm_with_system_prompt(
|
|
@@ -428,6 +408,8 @@ def generate_commit_message_for_prompt(
|
|
|
428
408
|
model=model,
|
|
429
409
|
provider_config=provider_config,
|
|
430
410
|
http_client_config=http_client_config,
|
|
411
|
+
disable_thinking=disable_thinking,
|
|
412
|
+
max_tokens=max_tokens,
|
|
431
413
|
)
|
|
432
414
|
|
|
433
415
|
|
|
@@ -461,6 +443,8 @@ def generate_commit_message_for_status(
|
|
|
461
443
|
context: str = "",
|
|
462
444
|
provider_config: providers.ProviderConfig | None = None,
|
|
463
445
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
446
|
+
disable_thinking: bool = True,
|
|
447
|
+
max_tokens: int = 1024,
|
|
464
448
|
) -> str:
|
|
465
449
|
"""Generate a commit message for a staged status snapshot."""
|
|
466
450
|
full_prompt = build_commit_message_prompt(status, context=context)
|
|
@@ -470,6 +454,8 @@ def generate_commit_message_for_status(
|
|
|
470
454
|
model=model,
|
|
471
455
|
provider_config=provider_config,
|
|
472
456
|
http_client_config=http_client_config,
|
|
457
|
+
disable_thinking=disable_thinking,
|
|
458
|
+
max_tokens=max_tokens,
|
|
473
459
|
)
|
|
474
460
|
except llm.LLMError as exc:
|
|
475
461
|
if not should_retry_with_compact_prompt(exc):
|
|
@@ -488,6 +474,8 @@ def generate_commit_message_for_status(
|
|
|
488
474
|
model=model,
|
|
489
475
|
provider_config=provider_config,
|
|
490
476
|
http_client_config=http_client_config,
|
|
477
|
+
disable_thinking=disable_thinking,
|
|
478
|
+
max_tokens=max_tokens,
|
|
491
479
|
)
|
|
492
480
|
|
|
493
481
|
|
|
@@ -506,13 +494,13 @@ def commit_with_retry_no_verify(
|
|
|
506
494
|
"Retry commit with [bold]`-n`[/] (skip hooks) using the same commit message?",
|
|
507
495
|
default=True,
|
|
508
496
|
):
|
|
509
|
-
raise
|
|
497
|
+
raise SystemExit(1)
|
|
510
498
|
|
|
511
499
|
try:
|
|
512
500
|
return repo.commit(message, use_editor=use_editor, no_verify=True, env=env)
|
|
513
501
|
except GitError as retry_error:
|
|
514
502
|
console.print(f"[red]Commit with -n failed: {retry_error}[/red]")
|
|
515
|
-
raise
|
|
503
|
+
raise SystemExit(1)
|
|
516
504
|
|
|
517
505
|
|
|
518
506
|
def ensure_copilot_authentication(
|
|
@@ -534,7 +522,7 @@ def ensure_copilot_authentication(
|
|
|
534
522
|
)
|
|
535
523
|
except copilot.LLMError as exc:
|
|
536
524
|
print_llm_error("Authentication failed", exc)
|
|
537
|
-
raise
|
|
525
|
+
raise SystemExit(1)
|
|
538
526
|
|
|
539
527
|
|
|
540
528
|
def stage_changes_for_commit(
|
|
@@ -575,6 +563,8 @@ def request_commit_message(
|
|
|
575
563
|
context: str = "",
|
|
576
564
|
provider_config: providers.ProviderConfig | None = None,
|
|
577
565
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
566
|
+
disable_thinking: bool = True,
|
|
567
|
+
max_tokens: int = 1024,
|
|
578
568
|
) -> str:
|
|
579
569
|
"""Request a commit message for the provided staged state."""
|
|
580
570
|
try:
|
|
@@ -587,10 +577,12 @@ def request_commit_message(
|
|
|
587
577
|
context=context,
|
|
588
578
|
provider_config=provider_config,
|
|
589
579
|
http_client_config=http_client_config,
|
|
580
|
+
disable_thinking=disable_thinking,
|
|
581
|
+
max_tokens=max_tokens,
|
|
590
582
|
)
|
|
591
583
|
except llm.LLMError as exc:
|
|
592
584
|
print_llm_error("Could not generate a commit message", exc)
|
|
593
|
-
raise
|
|
585
|
+
raise SystemExit(1)
|
|
594
586
|
|
|
595
587
|
|
|
596
588
|
def request_split_commit_plan(
|
|
@@ -602,6 +594,8 @@ def request_split_commit_plan(
|
|
|
602
594
|
context: str = "",
|
|
603
595
|
provider_config: providers.ProviderConfig | None = None,
|
|
604
596
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
597
|
+
disable_thinking: bool = True,
|
|
598
|
+
max_tokens: int = 1024,
|
|
605
599
|
) -> SplitCommitPlan:
|
|
606
600
|
"""Request and validate a split-commit plan for the staged patch units."""
|
|
607
601
|
planner_system_prompt = load_named_prompt(SPLIT_COMMIT_PLANNER_PROMPT_FILENAME)
|
|
@@ -622,11 +616,13 @@ def request_split_commit_plan(
|
|
|
622
616
|
model=model,
|
|
623
617
|
provider_config=provider_config,
|
|
624
618
|
http_client_config=http_client_config,
|
|
619
|
+
disable_thinking=disable_thinking,
|
|
620
|
+
max_tokens=max_tokens,
|
|
625
621
|
)
|
|
626
622
|
except llm.LLMError as exc:
|
|
627
623
|
if not should_retry_with_compact_prompt(exc):
|
|
628
624
|
print_llm_error("Could not generate a split commit plan", exc)
|
|
629
|
-
raise
|
|
625
|
+
raise SystemExit(1)
|
|
630
626
|
|
|
631
627
|
console.print(
|
|
632
628
|
"[yellow]Staged patch units exceeded the model context window; retrying split planning with summaries only.[/yellow]"
|
|
@@ -655,10 +651,12 @@ def request_split_commit_plan(
|
|
|
655
651
|
model=model,
|
|
656
652
|
provider_config=provider_config,
|
|
657
653
|
http_client_config=http_client_config,
|
|
654
|
+
disable_thinking=disable_thinking,
|
|
655
|
+
max_tokens=max_tokens,
|
|
658
656
|
)
|
|
659
657
|
except llm.LLMError as exc:
|
|
660
658
|
print_llm_error("Could not generate a split commit plan", exc)
|
|
661
|
-
raise
|
|
659
|
+
raise SystemExit(1)
|
|
662
660
|
|
|
663
661
|
return parse_split_plan_response(
|
|
664
662
|
response,
|
|
@@ -674,6 +672,8 @@ def request_split_commit_messages(
|
|
|
674
672
|
context: str = "",
|
|
675
673
|
provider_config: providers.ProviderConfig | None = None,
|
|
676
674
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
675
|
+
disable_thinking: bool = True,
|
|
676
|
+
max_tokens: int = 1024,
|
|
677
677
|
) -> list[PreparedSplitCommit]:
|
|
678
678
|
"""Generate commit messages for each planned split-commit group."""
|
|
679
679
|
try:
|
|
@@ -691,6 +691,8 @@ def request_split_commit_messages(
|
|
|
691
691
|
context=context,
|
|
692
692
|
provider_config=provider_config,
|
|
693
693
|
http_client_config=http_client_config,
|
|
694
|
+
disable_thinking=disable_thinking,
|
|
695
|
+
max_tokens=max_tokens,
|
|
694
696
|
)
|
|
695
697
|
|
|
696
698
|
prepared_commits.append(
|
|
@@ -700,7 +702,7 @@ def request_split_commit_messages(
|
|
|
700
702
|
return prepared_commits
|
|
701
703
|
except llm.LLMError as exc:
|
|
702
704
|
print_llm_error("Could not generate split commit messages", exc)
|
|
703
|
-
raise
|
|
705
|
+
raise SystemExit(1)
|
|
704
706
|
|
|
705
707
|
|
|
706
708
|
def confirm_split_commit_count(
|
|
@@ -730,7 +732,7 @@ def confirm_split_commit_count(
|
|
|
730
732
|
return plan
|
|
731
733
|
|
|
732
734
|
console.print("Split commit plan cancelled.")
|
|
733
|
-
raise
|
|
735
|
+
raise SystemExit()
|
|
734
736
|
|
|
735
737
|
|
|
736
738
|
def display_commit_message(commit_message: str) -> None:
|
|
@@ -769,7 +771,7 @@ def execute_commit_action(
|
|
|
769
771
|
if yes:
|
|
770
772
|
return commit_with_retry_no_verify(repo, commit_message)
|
|
771
773
|
|
|
772
|
-
choice =
|
|
774
|
+
choice = Prompt.ask(
|
|
773
775
|
"Choose action: (c)ommit, (e)dit message, (q)uit",
|
|
774
776
|
default="c",
|
|
775
777
|
show_default=True,
|
|
@@ -777,7 +779,7 @@ def execute_commit_action(
|
|
|
777
779
|
|
|
778
780
|
if choice == "q":
|
|
779
781
|
console.print("Commit cancelled.")
|
|
780
|
-
raise
|
|
782
|
+
raise SystemExit()
|
|
781
783
|
if choice == "e":
|
|
782
784
|
console.print("[cyan]Opening git editor...[/cyan]")
|
|
783
785
|
return commit_with_retry_no_verify(repo, commit_message, use_editor=True)
|
|
@@ -785,7 +787,7 @@ def execute_commit_action(
|
|
|
785
787
|
return commit_with_retry_no_verify(repo, commit_message)
|
|
786
788
|
|
|
787
789
|
console.print("Invalid choice. Commit cancelled.")
|
|
788
|
-
raise
|
|
790
|
+
raise SystemExit()
|
|
789
791
|
|
|
790
792
|
|
|
791
793
|
def execute_split_commit_plan(
|
|
@@ -797,7 +799,7 @@ def execute_split_commit_plan(
|
|
|
797
799
|
"""Run the split-commit plan against temporary alternate indexes."""
|
|
798
800
|
use_editor = False
|
|
799
801
|
if not yes:
|
|
800
|
-
choice =
|
|
802
|
+
choice = Prompt.ask(
|
|
801
803
|
"Choose action: (c)ommit all, (e)dit each message, (q)uit",
|
|
802
804
|
default="c",
|
|
803
805
|
show_default=True,
|
|
@@ -805,12 +807,12 @@ def execute_split_commit_plan(
|
|
|
805
807
|
|
|
806
808
|
if choice == "q":
|
|
807
809
|
console.print("Commit cancelled.")
|
|
808
|
-
raise
|
|
810
|
+
raise SystemExit()
|
|
809
811
|
if choice == "e":
|
|
810
812
|
use_editor = True
|
|
811
813
|
elif choice != "c":
|
|
812
814
|
console.print("Invalid choice. Commit cancelled.")
|
|
813
|
-
raise
|
|
815
|
+
raise SystemExit()
|
|
814
816
|
|
|
815
817
|
execution_state = SplitCommitExecutionState(
|
|
816
818
|
original_head_sha=repo.get_head_sha() if repo.has_commit("HEAD") else None,
|
|
@@ -840,16 +842,19 @@ def execute_split_commit_plan(
|
|
|
840
842
|
console.print(
|
|
841
843
|
f"[red]Failed to apply the planned changes for commit {index}: {exc}[/red]"
|
|
842
844
|
)
|
|
843
|
-
raise
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
repo
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
845
|
+
raise SystemExit(1)
|
|
846
|
+
|
|
847
|
+
try:
|
|
848
|
+
commit_shas.append(
|
|
849
|
+
repo.create_commit_from_index(
|
|
850
|
+
prepared_commit.message,
|
|
851
|
+
index=alternate_index,
|
|
852
|
+
use_editor=use_editor,
|
|
853
|
+
)
|
|
851
854
|
)
|
|
852
|
-
|
|
855
|
+
except GitError as exc:
|
|
856
|
+
console.print(f"[red]Failed to create commit {index}: {exc}[/red]")
|
|
857
|
+
raise SystemExit(1)
|
|
853
858
|
except BaseException:
|
|
854
859
|
try:
|
|
855
860
|
if execution_state.original_head_sha is not None:
|
|
@@ -881,6 +886,8 @@ def handle_single_commit_flow(
|
|
|
881
886
|
context: str = "",
|
|
882
887
|
provider_config: providers.ProviderConfig | None = None,
|
|
883
888
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
889
|
+
disable_thinking: bool = True,
|
|
890
|
+
max_tokens: int = 1024,
|
|
884
891
|
) -> None:
|
|
885
892
|
"""Generate, display, and execute the single-commit flow."""
|
|
886
893
|
commit_message = request_commit_message(
|
|
@@ -889,6 +896,8 @@ def handle_single_commit_flow(
|
|
|
889
896
|
context=context,
|
|
890
897
|
provider_config=provider_config,
|
|
891
898
|
http_client_config=http_client_config,
|
|
899
|
+
disable_thinking=disable_thinking,
|
|
900
|
+
max_tokens=max_tokens,
|
|
892
901
|
)
|
|
893
902
|
display_commit_message(commit_message)
|
|
894
903
|
|
|
@@ -906,6 +915,8 @@ def handle_split_commit_flow(
|
|
|
906
915
|
context: str = "",
|
|
907
916
|
provider_config: providers.ProviderConfig | None = None,
|
|
908
917
|
http_client_config: llm.HttpClientConfig | None = None,
|
|
918
|
+
disable_thinking: bool = True,
|
|
919
|
+
max_tokens: int = 1024,
|
|
909
920
|
) -> None:
|
|
910
921
|
"""Generate, display, and execute the split-commit flow."""
|
|
911
922
|
patch_units = tuple(
|
|
@@ -924,6 +935,8 @@ def handle_split_commit_flow(
|
|
|
924
935
|
context=context,
|
|
925
936
|
provider_config=provider_config,
|
|
926
937
|
http_client_config=http_client_config,
|
|
938
|
+
disable_thinking=disable_thinking,
|
|
939
|
+
max_tokens=max_tokens,
|
|
927
940
|
)
|
|
928
941
|
return
|
|
929
942
|
|
|
@@ -939,6 +952,8 @@ def handle_split_commit_flow(
|
|
|
939
952
|
context=context,
|
|
940
953
|
provider_config=provider_config,
|
|
941
954
|
http_client_config=http_client_config,
|
|
955
|
+
disable_thinking=disable_thinking,
|
|
956
|
+
max_tokens=max_tokens,
|
|
942
957
|
)
|
|
943
958
|
return
|
|
944
959
|
|
|
@@ -961,6 +976,8 @@ def handle_split_commit_flow(
|
|
|
961
976
|
context=context,
|
|
962
977
|
provider_config=provider_config,
|
|
963
978
|
http_client_config=http_client_config,
|
|
979
|
+
disable_thinking=disable_thinking,
|
|
980
|
+
max_tokens=max_tokens,
|
|
964
981
|
)
|
|
965
982
|
except SplitPlanningError as exc:
|
|
966
983
|
console.print(
|
|
@@ -975,6 +992,8 @@ def handle_split_commit_flow(
|
|
|
975
992
|
context=context,
|
|
976
993
|
provider_config=provider_config,
|
|
977
994
|
http_client_config=http_client_config,
|
|
995
|
+
disable_thinking=disable_thinking,
|
|
996
|
+
max_tokens=max_tokens,
|
|
978
997
|
)
|
|
979
998
|
return
|
|
980
999
|
|
|
@@ -992,6 +1011,8 @@ def handle_split_commit_flow(
|
|
|
992
1011
|
context=context,
|
|
993
1012
|
provider_config=provider_config,
|
|
994
1013
|
http_client_config=http_client_config,
|
|
1014
|
+
disable_thinking=disable_thinking,
|
|
1015
|
+
max_tokens=max_tokens,
|
|
995
1016
|
)
|
|
996
1017
|
prepared_commits = order_prepared_split_commits(prepared_commits)
|
|
997
1018
|
|
|
@@ -1012,22 +1033,29 @@ def handle_split_commit_flow(
|
|
|
1012
1033
|
console.print(f"[green]{commit_sha[:8]}[/green] {prepared_commit.message}")
|
|
1013
1034
|
|
|
1014
1035
|
|
|
1015
|
-
@app.command("authenticate")
|
|
1016
|
-
@app.command("login",
|
|
1036
|
+
@app.command(name="authenticate")
|
|
1037
|
+
@app.command(name="login", show=False)
|
|
1017
1038
|
def authenticate(
|
|
1018
|
-
enterprise_domain:
|
|
1019
|
-
None,
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1039
|
+
enterprise_domain: Annotated[
|
|
1040
|
+
str | None,
|
|
1041
|
+
cyclopts.Parameter(
|
|
1042
|
+
name="--enterprise-domain",
|
|
1043
|
+
help="GitHub Enterprise hostname. Omit for github.com.",
|
|
1044
|
+
),
|
|
1045
|
+
] = None,
|
|
1046
|
+
force: Annotated[
|
|
1047
|
+
bool,
|
|
1048
|
+
cyclopts.Parameter(
|
|
1049
|
+
name="--force",
|
|
1050
|
+
help="Replace cached GitHub Copilot credentials",
|
|
1051
|
+
),
|
|
1052
|
+
] = False,
|
|
1026
1053
|
ca_bundle: CaBundleOption = None,
|
|
1027
1054
|
insecure: InsecureOption = False,
|
|
1028
|
-
native_tls: NativeTlsOption =
|
|
1055
|
+
native_tls: NativeTlsOption = True,
|
|
1029
1056
|
):
|
|
1030
1057
|
"""Authenticate with GitHub Copilot and cache credentials locally."""
|
|
1058
|
+
print_cli_banner()
|
|
1031
1059
|
http_client_config = build_http_client_config(
|
|
1032
1060
|
ca_bundle=ca_bundle,
|
|
1033
1061
|
insecure=insecure,
|
|
@@ -1041,19 +1069,20 @@ def authenticate(
|
|
|
1041
1069
|
)
|
|
1042
1070
|
except copilot.LLMError as exc:
|
|
1043
1071
|
print_llm_error("Authentication failed", exc)
|
|
1044
|
-
raise
|
|
1072
|
+
raise SystemExit(1)
|
|
1045
1073
|
|
|
1046
1074
|
|
|
1047
|
-
@app.command("summary")
|
|
1075
|
+
@app.command(name="summary")
|
|
1048
1076
|
def summary(
|
|
1049
1077
|
provider: ProviderOption = None,
|
|
1050
1078
|
base_url: BaseUrlOption = None,
|
|
1051
1079
|
api_key: ApiKeyOption = None,
|
|
1052
1080
|
ca_bundle: CaBundleOption = None,
|
|
1053
1081
|
insecure: InsecureOption = False,
|
|
1054
|
-
native_tls: NativeTlsOption =
|
|
1082
|
+
native_tls: NativeTlsOption = True,
|
|
1055
1083
|
):
|
|
1056
1084
|
"""Show the configured LLM provider summary."""
|
|
1085
|
+
print_cli_banner()
|
|
1057
1086
|
http_client_config = build_http_client_config(
|
|
1058
1087
|
ca_bundle=ca_bundle,
|
|
1059
1088
|
insecure=insecure,
|
|
@@ -1071,24 +1100,27 @@ def summary(
|
|
|
1071
1100
|
)
|
|
1072
1101
|
except llm.LLMError as exc:
|
|
1073
1102
|
print_llm_error("Could not load provider summary", exc)
|
|
1074
|
-
raise
|
|
1103
|
+
raise SystemExit(1)
|
|
1075
1104
|
|
|
1076
1105
|
|
|
1077
|
-
@app.command("models")
|
|
1106
|
+
@app.command(name="models")
|
|
1078
1107
|
def models_command(
|
|
1079
1108
|
provider: ProviderOption = None,
|
|
1080
1109
|
base_url: BaseUrlOption = None,
|
|
1081
1110
|
api_key: ApiKeyOption = None,
|
|
1082
|
-
vendor:
|
|
1083
|
-
None,
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1111
|
+
vendor: Annotated[
|
|
1112
|
+
str | None,
|
|
1113
|
+
cyclopts.Parameter(
|
|
1114
|
+
name="--vendor",
|
|
1115
|
+
help="Filter listed models by vendor: anthropic, gemini/google, or openai.",
|
|
1116
|
+
),
|
|
1117
|
+
] = None,
|
|
1087
1118
|
ca_bundle: CaBundleOption = None,
|
|
1088
1119
|
insecure: InsecureOption = False,
|
|
1089
|
-
native_tls: NativeTlsOption =
|
|
1120
|
+
native_tls: NativeTlsOption = True,
|
|
1090
1121
|
):
|
|
1091
1122
|
"""List available models for the configured LLM provider."""
|
|
1123
|
+
print_cli_banner()
|
|
1092
1124
|
http_client_config = build_http_client_config(
|
|
1093
1125
|
ca_bundle=ca_bundle,
|
|
1094
1126
|
insecure=insecure,
|
|
@@ -1116,47 +1148,75 @@ def models_command(
|
|
|
1116
1148
|
)
|
|
1117
1149
|
except llm.LLMError as exc:
|
|
1118
1150
|
print_llm_error("Could not load models", exc)
|
|
1119
|
-
raise
|
|
1151
|
+
raise SystemExit(1)
|
|
1120
1152
|
|
|
1121
1153
|
|
|
1122
|
-
@app.command
|
|
1154
|
+
@app.command
|
|
1123
1155
|
def commit(
|
|
1124
|
-
all_files:
|
|
1125
|
-
|
|
1126
|
-
|
|
1156
|
+
all_files: Annotated[
|
|
1157
|
+
bool,
|
|
1158
|
+
cyclopts.Parameter(
|
|
1159
|
+
name=("--all", "-a"),
|
|
1160
|
+
help="Stage all files before committing",
|
|
1161
|
+
),
|
|
1162
|
+
] = False,
|
|
1127
1163
|
split: SplitOption = False,
|
|
1128
1164
|
split_count: SplitCountOption = None,
|
|
1129
|
-
model:
|
|
1130
|
-
None,
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
yes:
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1165
|
+
model: Annotated[
|
|
1166
|
+
str | None,
|
|
1167
|
+
cyclopts.Parameter(
|
|
1168
|
+
name=("--model", "-m"),
|
|
1169
|
+
help="Model to use for generating commit message",
|
|
1170
|
+
),
|
|
1171
|
+
] = None,
|
|
1172
|
+
yes: Annotated[
|
|
1173
|
+
bool,
|
|
1174
|
+
cyclopts.Parameter(
|
|
1175
|
+
name=("--yes", "-y"),
|
|
1176
|
+
help="Automatically accept the generated commit message",
|
|
1177
|
+
),
|
|
1178
|
+
] = False,
|
|
1179
|
+
context: Annotated[
|
|
1180
|
+
str,
|
|
1181
|
+
cyclopts.Parameter(
|
|
1182
|
+
name=("--context", "-c"),
|
|
1183
|
+
help="Optional user-provided context to guide commit message",
|
|
1184
|
+
),
|
|
1185
|
+
] = "",
|
|
1186
|
+
disable_thinking: Annotated[
|
|
1187
|
+
bool,
|
|
1188
|
+
cyclopts.Parameter(
|
|
1189
|
+
name="--disable-thinking",
|
|
1190
|
+
negative="--enable-thinking",
|
|
1191
|
+
help=(
|
|
1192
|
+
"Disable or minimize reasoning/thinking tokens for commit-message requests."
|
|
1193
|
+
),
|
|
1194
|
+
),
|
|
1195
|
+
] = True,
|
|
1196
|
+
max_tokens: Annotated[
|
|
1197
|
+
int,
|
|
1198
|
+
cyclopts.Parameter(
|
|
1199
|
+
name="--max-tokens",
|
|
1200
|
+
help=("Maximum output tokens for LLM generation."),
|
|
1201
|
+
validator=cyclopts.validators.Number(gte=1),
|
|
1202
|
+
),
|
|
1203
|
+
] = 1024,
|
|
1145
1204
|
provider: ProviderOption = None,
|
|
1146
1205
|
base_url: BaseUrlOption = None,
|
|
1147
1206
|
api_key: ApiKeyOption = None,
|
|
1148
1207
|
ca_bundle: CaBundleOption = None,
|
|
1149
1208
|
insecure: InsecureOption = False,
|
|
1150
|
-
native_tls: NativeTlsOption =
|
|
1209
|
+
native_tls: NativeTlsOption = True,
|
|
1151
1210
|
):
|
|
1152
1211
|
"""
|
|
1153
1212
|
Generate commit message based on changes in the current git repository and commit them.
|
|
1154
1213
|
"""
|
|
1214
|
+
print_cli_banner()
|
|
1155
1215
|
try:
|
|
1156
1216
|
repo = GitRepository()
|
|
1157
1217
|
except NotAGitRepositoryError:
|
|
1158
1218
|
console.print("[red]Error: Not in a git repository[/red]")
|
|
1159
|
-
raise
|
|
1219
|
+
raise SystemExit(1)
|
|
1160
1220
|
|
|
1161
1221
|
http_client_config = build_http_client_config(
|
|
1162
1222
|
ca_bundle=ca_bundle,
|
|
@@ -1171,7 +1231,7 @@ def commit(
|
|
|
1171
1231
|
)
|
|
1172
1232
|
except llm.LLMError as exc:
|
|
1173
1233
|
print_llm_error("Could not resolve the LLM provider", exc)
|
|
1174
|
-
raise
|
|
1234
|
+
raise SystemExit(1)
|
|
1175
1235
|
|
|
1176
1236
|
if provider_config.provider == "copilot":
|
|
1177
1237
|
ensure_copilot_authentication(http_client_config)
|
|
@@ -1181,7 +1241,7 @@ def commit(
|
|
|
1181
1241
|
|
|
1182
1242
|
if not status.files:
|
|
1183
1243
|
console.print("[yellow]No changes to commit.[/yellow]")
|
|
1184
|
-
raise
|
|
1244
|
+
raise SystemExit()
|
|
1185
1245
|
|
|
1186
1246
|
status = stage_changes_for_commit(repo, status, all_files=all_files)
|
|
1187
1247
|
|
|
@@ -1199,7 +1259,7 @@ def commit(
|
|
|
1199
1259
|
)
|
|
1200
1260
|
except llm.LLMError as exc:
|
|
1201
1261
|
print_llm_error("Could not select a model", exc)
|
|
1202
|
-
raise
|
|
1262
|
+
raise SystemExit(1)
|
|
1203
1263
|
|
|
1204
1264
|
display_selected_model(selected_model)
|
|
1205
1265
|
model = selected_model.id
|
|
@@ -1214,6 +1274,8 @@ def commit(
|
|
|
1214
1274
|
context=context,
|
|
1215
1275
|
provider_config=provider_config,
|
|
1216
1276
|
http_client_config=http_client_config,
|
|
1277
|
+
disable_thinking=disable_thinking,
|
|
1278
|
+
max_tokens=max_tokens,
|
|
1217
1279
|
)
|
|
1218
1280
|
return
|
|
1219
1281
|
|
|
@@ -1225,6 +1287,8 @@ def commit(
|
|
|
1225
1287
|
context=context,
|
|
1226
1288
|
provider_config=provider_config,
|
|
1227
1289
|
http_client_config=http_client_config,
|
|
1290
|
+
disable_thinking=disable_thinking,
|
|
1291
|
+
max_tokens=max_tokens,
|
|
1228
1292
|
)
|
|
1229
1293
|
|
|
1230
1294
|
|