git-copilot-commit 0.3.6__tar.gz → 0.4.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.
Files changed (19) hide show
  1. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/PKG-INFO +2 -2
  2. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/pyproject.toml +1 -1
  3. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/src/git_copilot_commit/cli.py +40 -98
  4. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/src/git_copilot_commit/git.py +15 -3
  5. git_copilot_commit-0.4.0/src/git_copilot_commit/github_copilot.py +824 -0
  6. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/src/git_copilot_commit/settings.py +1 -1
  7. git_copilot_commit-0.4.0/uv.lock +205 -0
  8. git_copilot_commit-0.3.6/uv.lock +0 -1174
  9. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/.github/workflows/ci.yml +0 -0
  10. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/.gitignore +0 -0
  11. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/.justfile +0 -0
  12. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/.python-version +0 -0
  13. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/LICENSE +0 -0
  14. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/README.md +0 -0
  15. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/src/git_copilot_commit/__init__.py +0 -0
  16. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/src/git_copilot_commit/prompts/commit-message-generator-prompt.md +0 -0
  17. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/src/git_copilot_commit/py.typed +0 -0
  18. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/src/git_copilot_commit/version.py +0 -0
  19. {git_copilot_commit-0.3.6 → git_copilot_commit-0.4.0}/vhs/demo.vhs +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-copilot-commit
3
- Version: 0.3.6
3
+ Version: 0.4.0
4
4
  Summary: Automatically generate and commit changes using copilot
5
5
  Author-email: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com>
6
6
  License-File: LICENSE
7
7
  Requires-Python: >=3.12
8
- Requires-Dist: litellm>=1.75.4
8
+ Requires-Dist: httpx>=0.28.0
9
9
  Requires-Dist: platformdirs>=4.0.0
10
10
  Requires-Dist: rich>=14.0.0
11
11
  Requires-Dist: typer>=0.16.0
@@ -8,10 +8,10 @@ authors = [
8
8
  ]
9
9
  requires-python = ">=3.12"
10
10
  dependencies = [
11
+ "httpx>=0.28.0",
11
12
  "rich>=14.0.0",
12
13
  "typer>=0.16.0",
13
14
  "platformdirs>=4.0.0",
14
- "litellm>=1.75.4",
15
15
  ]
16
16
 
17
17
  [project.scripts]
@@ -2,18 +2,18 @@
2
2
  git-copilot-commit - AI-powered Git commit assistant
3
3
  """
4
4
 
5
- import rich.terminal_theme
5
+ from pathlib import Path
6
+
7
+ import rich
6
8
  import typer
7
9
  from rich.console import Console
8
- from rich.prompt import Confirm
9
10
  from rich.panel import Panel
10
- import rich
11
- from pathlib import Path
11
+ from rich.prompt import Confirm
12
12
 
13
- from litellm import completion
14
13
  from .git import GitRepository, GitError, NotAGitRepositoryError
15
14
  from .settings import Settings
16
15
  from .version import __version__
16
+ from . import github_copilot
17
17
 
18
18
  console = Console()
19
19
  app = typer.Typer(help=__doc__, add_completion=False)
@@ -84,29 +84,6 @@ def load_system_prompt() -> str:
84
84
  raise typer.Exit(1)
85
85
 
86
86
 
87
- def ask(prompt, model) -> str:
88
- response = completion(
89
- model=model,
90
- messages=[
91
- {"role": "system", "content": load_system_prompt()},
92
- {"role": "user", "content": prompt},
93
- ],
94
- extra_headers={
95
- "editor-version": "vscode/1.85.1",
96
- "Copilot-Integration-Id": "vscode-chat",
97
- },
98
- )
99
- text = response.choices[0].message.content
100
- text = text.strip()
101
- # Remove triple backticks if they wrap the entire text
102
- if text.startswith("```") and text.endswith("```"):
103
- text = text[3:-3].strip()
104
- # Otherwise remove single backticks if they wrap the entire text
105
- elif text.startswith("`") and text.endswith("`"):
106
- text = text[1:-1].strip()
107
- return text
108
-
109
-
110
87
  def generate_commit_message(
111
88
  repo: GitRepository, model: str | None = None, context: str = ""
112
89
  ) -> str:
@@ -134,35 +111,33 @@ def generate_commit_message(
134
111
  prompt = "\n".join(prompt_parts)
135
112
 
136
113
  if model is None:
137
- model = "github_copilot/gpt-4"
114
+ model = "gpt-5.1-codex"
138
115
 
139
- if not model.startswith("github_copilot/"):
140
- model = f"github_copilot/{model}"
116
+ if model.startswith("github_copilot/"):
117
+ model = model.replace("github_copilot/", "")
141
118
 
142
- try:
143
- return ask(prompt, model=model)
144
- except Exception as _:
145
- console.print(
146
- "Prompt failed, falling back to simpler commit message generation."
147
- )
148
-
149
- fallback_prompt_parts = [
150
- "`git status`:\n",
151
- f"```\n{status.get_porcelain_output()}\n```",
152
- ]
153
-
154
- if context.strip():
155
- fallback_prompt_parts.insert(
156
- 0, f"User-provided context:\n\n{context.strip()}\n\n"
157
- )
119
+ return github_copilot.ask(prompt, model=model)
158
120
 
159
- fallback_prompt_parts.append(
160
- "\nGenerate a conventional commit message based on the git status above:"
161
- )
162
121
 
163
- fallback_prompt = "\n".join(fallback_prompt_parts)
122
+ def commit_with_retry_no_verify(
123
+ repo: GitRepository, message: str, use_editor: bool = False
124
+ ) -> str:
125
+ """Run commit and offer one retry with -n on failure."""
126
+ try:
127
+ return repo.commit(message, use_editor=use_editor)
128
+ except GitError as e:
129
+ console.print(f"[red]Commit failed: {e}[/red]")
130
+ if not Confirm.ask(
131
+ "Retry commit with [bold]`-n`[/] (skip hooks) using the same commit message?",
132
+ default=True,
133
+ ):
134
+ raise typer.Exit(1)
164
135
 
165
- return ask(fallback_prompt, model=model)
136
+ try:
137
+ return repo.commit(message, use_editor=use_editor, no_verify=True)
138
+ except GitError as retry_error:
139
+ console.print(f"[red]Commit with -n failed: {retry_error}[/red]")
140
+ raise typer.Exit(1)
166
141
 
167
142
 
168
143
  @app.command()
@@ -192,6 +167,14 @@ def commit(
192
167
  console.print("[red]Error: Not in a git repository[/red]")
193
168
  raise typer.Exit(1)
194
169
 
170
+ try:
171
+ existing_credentials = github_copilot.load_credentials()
172
+ except Exception as _:
173
+ existing_credentials = None
174
+
175
+ if existing_credentials is None:
176
+ github_copilot.login()
177
+
195
178
  # Load settings and use default model if none provided
196
179
  settings = Settings()
197
180
  if model is None:
@@ -255,11 +238,7 @@ def commit(
255
238
  # Confirm commit or edit message (skip if --yes flag is used)
256
239
  if yes:
257
240
  # Automatically commit with generated message
258
- try:
259
- commit_sha = repo.commit(commit_message)
260
- except GitError as e:
261
- console.print(f"[red]Commit failed: {e}[/red]")
262
- raise typer.Exit(1)
241
+ commit_sha = commit_with_retry_no_verify(repo, commit_message)
263
242
  else:
264
243
  choice = typer.prompt(
265
244
  "Choose action: (c)ommit, (e)dit message, (q)uit",
@@ -273,18 +252,12 @@ def commit(
273
252
  elif choice == "e":
274
253
  # Use git's built-in editor with generated message as template
275
254
  console.print("[cyan]Opening git editor...[/cyan]")
276
- try:
277
- commit_sha = repo.commit(commit_message, use_editor=True)
278
- except GitError as e:
279
- console.print(f"[red]Commit failed: {e}[/red]")
280
- raise typer.Exit(1)
255
+ commit_sha = commit_with_retry_no_verify(
256
+ repo, commit_message, use_editor=True
257
+ )
281
258
  elif choice == "c":
282
259
  # Commit with generated message
283
- try:
284
- commit_sha = repo.commit(commit_message)
285
- except GitError as e:
286
- console.print(f"[red]Commit failed: {e}[/red]")
287
- raise typer.Exit(1)
260
+ commit_sha = commit_with_retry_no_verify(repo, commit_message)
288
261
  else:
289
262
  console.print("Invalid choice. Commit cancelled.")
290
263
  raise typer.Exit()
@@ -293,36 +266,5 @@ def commit(
293
266
  console.print(f"[green]✓ Successfully committed: {commit_sha[:8]}[/green]")
294
267
 
295
268
 
296
- @app.command()
297
- def config(
298
- set_default_model: str | None = typer.Option(
299
- None, "--set-default-model", help="Set default model for commit messages"
300
- ),
301
- show: bool = typer.Option(False, "--show", help="Show current configuration"),
302
- ):
303
- """Manage application configuration."""
304
- settings = Settings()
305
-
306
- if set_default_model:
307
- settings.default_model = set_default_model
308
- console.print(f"[green]✓ Default model set to: {set_default_model}[/green]")
309
-
310
- if show or (not set_default_model):
311
- console.print("\n[bold]Current Configuration:[/bold]")
312
- default_model = settings.default_model
313
- if default_model:
314
- console.print(f"Default model: [cyan]{default_model}[/cyan]")
315
- else:
316
- console.print("Default model: [dim]not set[/dim]")
317
-
318
- active_prompt = get_active_prompt_path()
319
- if active_prompt:
320
- console.print(f"Active prompt file: [cyan]{active_prompt}[/cyan]")
321
- else:
322
- console.print("Active prompt file: [red]not found[/red]")
323
-
324
- console.print(f"Config file: [dim]{settings.config_file}[/dim]")
325
-
326
-
327
269
  if __name__ == "__main__":
328
270
  app()
@@ -226,13 +226,19 @@ class GitRepository:
226
226
  else:
227
227
  self._run_git_command(["reset", "HEAD"] + paths)
228
228
 
229
- def commit(self, message: str | None = None, use_editor: bool = False) -> str:
229
+ def commit(
230
+ self,
231
+ message: str | None = None,
232
+ use_editor: bool = False,
233
+ no_verify: bool = False,
234
+ ) -> str:
230
235
  """
231
236
  Create a commit with the given message or using git's editor.
232
237
 
233
238
  Args:
234
239
  message: Commit message. Used as template if use_editor is True.
235
240
  use_editor: Whether to use git's configured editor.
241
+ no_verify: Skip pre-commit and commit-msg hooks (git commit -n).
236
242
 
237
243
  Returns:
238
244
  Commit SHA.
@@ -253,7 +259,10 @@ class GitRepository:
253
259
  temp_file = f.name
254
260
 
255
261
  try:
256
- args = ["commit", "-e", "-F", temp_file]
262
+ args = ["commit"]
263
+ if no_verify:
264
+ args.append("-n")
265
+ args.extend(["-e", "-F", temp_file])
257
266
 
258
267
  # Run interactively without capturing output
259
268
  cmd = ["git"] + args
@@ -273,7 +282,10 @@ class GitRepository:
273
282
  else:
274
283
  if message is None:
275
284
  raise ValueError("message is required when use_editor is False")
276
- args = ["commit", "-m", message]
285
+ args = ["commit"]
286
+ if no_verify:
287
+ args.append("-n")
288
+ args.extend(["-m", message])
277
289
 
278
290
  self._run_git_command(args)
279
291