gac 2.0.0__tar.gz → 2.1.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.

Potentially problematic release.


This version of gac might be problematic. Click here for more details.

Files changed (43) hide show
  1. {gac-2.0.0 → gac-2.1.0}/PKG-INFO +13 -7
  2. {gac-2.0.0 → gac-2.1.0}/README.md +12 -6
  3. {gac-2.0.0 → gac-2.1.0}/src/gac/__version__.py +1 -1
  4. {gac-2.0.0 → gac-2.1.0}/src/gac/ai.py +2 -0
  5. {gac-2.0.0 → gac-2.1.0}/src/gac/ai_utils.py +1 -0
  6. {gac-2.0.0 → gac-2.1.0}/src/gac/cli.py +10 -0
  7. {gac-2.0.0 → gac-2.1.0}/src/gac/constants.py +31 -0
  8. {gac-2.0.0 → gac-2.1.0}/src/gac/init_cli.py +6 -32
  9. {gac-2.0.0 → gac-2.1.0}/src/gac/language_cli.py +4 -33
  10. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/__init__.py +2 -0
  11. gac-2.1.0/src/gac/providers/mistral.py +38 -0
  12. {gac-2.0.0 → gac-2.1.0}/.gitignore +0 -0
  13. {gac-2.0.0 → gac-2.1.0}/LICENSE +0 -0
  14. {gac-2.0.0 → gac-2.1.0}/pyproject.toml +0 -0
  15. {gac-2.0.0 → gac-2.1.0}/src/gac/__init__.py +0 -0
  16. {gac-2.0.0 → gac-2.1.0}/src/gac/config.py +0 -0
  17. {gac-2.0.0 → gac-2.1.0}/src/gac/config_cli.py +0 -0
  18. {gac-2.0.0 → gac-2.1.0}/src/gac/diff_cli.py +0 -0
  19. {gac-2.0.0 → gac-2.1.0}/src/gac/errors.py +0 -0
  20. {gac-2.0.0 → gac-2.1.0}/src/gac/git.py +0 -0
  21. {gac-2.0.0 → gac-2.1.0}/src/gac/main.py +0 -0
  22. {gac-2.0.0 → gac-2.1.0}/src/gac/preprocess.py +0 -0
  23. {gac-2.0.0 → gac-2.1.0}/src/gac/prompt.py +0 -0
  24. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/anthropic.py +0 -0
  25. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/cerebras.py +0 -0
  26. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/chutes.py +0 -0
  27. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/custom_anthropic.py +0 -0
  28. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/custom_openai.py +0 -0
  29. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/deepseek.py +0 -0
  30. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/fireworks.py +0 -0
  31. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/gemini.py +0 -0
  32. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/groq.py +0 -0
  33. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/lmstudio.py +0 -0
  34. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/minimax.py +0 -0
  35. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/ollama.py +0 -0
  36. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/openai.py +0 -0
  37. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/openrouter.py +0 -0
  38. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/streamlake.py +0 -0
  39. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/synthetic.py +0 -0
  40. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/together.py +0 -0
  41. {gac-2.0.0 → gac-2.1.0}/src/gac/providers/zai.py +0 -0
  42. {gac-2.0.0 → gac-2.1.0}/src/gac/security.py +0 -0
  43. {gac-2.0.0 → gac-2.1.0}/src/gac/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gac
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: LLM-powered Git commit message generator with multi-provider support
5
5
  Project-URL: Homepage, https://github.com/cellwebb/gac
6
6
  Project-URL: Documentation, https://github.com/cellwebb/gac#readme
@@ -52,9 +52,9 @@ Description-Content-Type: text/markdown
52
52
  [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](docs/CONTRIBUTING.md)
53
53
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
54
54
 
55
- **LLM-powered commit messages that understand your code.**
55
+ **LLM-powered commit messages that understand your code!**
56
56
 
57
- **Tired of writing commit messages?** Replace `git commit -m "..."` with `gac` for contextual, well-formatted commit messages generated by large language models.
57
+ **Automate your commits!** Replace `git commit -m "..."` with `gac` for contextual, well-formatted commit messages generated by large language models!
58
58
 
59
59
  ---
60
60
 
@@ -68,7 +68,7 @@ Intelligent, contextual messages that explain the **why** behind your changes:
68
68
 
69
69
  ## Quick Start
70
70
 
71
- ### Use without installing
71
+ ### Use gac without installing
72
72
 
73
73
  ```bash
74
74
  uvx gac init # Configure your LLM provider
@@ -77,7 +77,7 @@ uvx gac # Generate and commit with LLM
77
77
 
78
78
  That's it! Review the generated message and confirm with `y`.
79
79
 
80
- ### Install gac globally
80
+ ### Install and use gac
81
81
 
82
82
  ```bash
83
83
  uv tool install gac
@@ -85,6 +85,12 @@ gac init
85
85
  gac
86
86
  ```
87
87
 
88
+ ### Upgrade installed gac
89
+
90
+ ```bash
91
+ uv tool upgrade gac
92
+ ```
93
+
88
94
  ---
89
95
 
90
96
  ## Key Features
@@ -92,7 +98,7 @@ gac
92
98
  ### 🌐 **Supported Providers**
93
99
 
94
100
  - **Anthropic** • **Cerebras** • **Chutes.ai** • **DeepSeek** • **Fireworks**
95
- - **Gemini** • **Groq** • **LM Studio** • **MiniMax** • **Ollama** • **OpenAI**
101
+ - **Gemini** • **Groq** • **LM Studio** • **MiniMax** • **Mistral** • **Ollama** • **OpenAI**
96
102
  - **OpenRouter** • **Streamlake** • **Synthetic.new** • **Together AI**
97
103
  - **Z.AI** • **Z.AI Coding** • **Custom Endpoints (Anthropic/OpenAI)**
98
104
 
@@ -183,7 +189,7 @@ Not happy with the result? You have two options:
183
189
  # Simple reroll (no feedback)
184
190
  r
185
191
 
186
- # Or just type your feedback directly - no prefix needed!
192
+ # Or just type your feedback directly!
187
193
  make it shorter and focus on the performance improvement
188
194
  use conventional commit format with scope
189
195
  explain the security implications
@@ -11,9 +11,9 @@
11
11
  [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](docs/CONTRIBUTING.md)
12
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
13
13
 
14
- **LLM-powered commit messages that understand your code.**
14
+ **LLM-powered commit messages that understand your code!**
15
15
 
16
- **Tired of writing commit messages?** Replace `git commit -m "..."` with `gac` for contextual, well-formatted commit messages generated by large language models.
16
+ **Automate your commits!** Replace `git commit -m "..."` with `gac` for contextual, well-formatted commit messages generated by large language models!
17
17
 
18
18
  ---
19
19
 
@@ -27,7 +27,7 @@ Intelligent, contextual messages that explain the **why** behind your changes:
27
27
 
28
28
  ## Quick Start
29
29
 
30
- ### Use without installing
30
+ ### Use gac without installing
31
31
 
32
32
  ```bash
33
33
  uvx gac init # Configure your LLM provider
@@ -36,7 +36,7 @@ uvx gac # Generate and commit with LLM
36
36
 
37
37
  That's it! Review the generated message and confirm with `y`.
38
38
 
39
- ### Install gac globally
39
+ ### Install and use gac
40
40
 
41
41
  ```bash
42
42
  uv tool install gac
@@ -44,6 +44,12 @@ gac init
44
44
  gac
45
45
  ```
46
46
 
47
+ ### Upgrade installed gac
48
+
49
+ ```bash
50
+ uv tool upgrade gac
51
+ ```
52
+
47
53
  ---
48
54
 
49
55
  ## Key Features
@@ -51,7 +57,7 @@ gac
51
57
  ### 🌐 **Supported Providers**
52
58
 
53
59
  - **Anthropic** • **Cerebras** • **Chutes.ai** • **DeepSeek** • **Fireworks**
54
- - **Gemini** • **Groq** • **LM Studio** • **MiniMax** • **Ollama** • **OpenAI**
60
+ - **Gemini** • **Groq** • **LM Studio** • **MiniMax** • **Mistral** • **Ollama** • **OpenAI**
55
61
  - **OpenRouter** • **Streamlake** • **Synthetic.new** • **Together AI**
56
62
  - **Z.AI** • **Z.AI Coding** • **Custom Endpoints (Anthropic/OpenAI)**
57
63
 
@@ -142,7 +148,7 @@ Not happy with the result? You have two options:
142
148
  # Simple reroll (no feedback)
143
149
  r
144
150
 
145
- # Or just type your feedback directly - no prefix needed!
151
+ # Or just type your feedback directly!
146
152
  make it shorter and focus on the performance improvement
147
153
  use conventional commit format with scope
148
154
  explain the security implications
@@ -1,3 +1,3 @@
1
1
  """Version information for gac package."""
2
2
 
3
- __version__ = "2.0.0"
3
+ __version__ = "2.1.0"
@@ -21,6 +21,7 @@ from gac.providers import (
21
21
  call_groq_api,
22
22
  call_lmstudio_api,
23
23
  call_minimax_api,
24
+ call_mistral_api,
24
25
  call_ollama_api,
25
26
  call_openai_api,
26
27
  call_openrouter_api,
@@ -94,6 +95,7 @@ def generate_commit_message(
94
95
  "groq": call_groq_api,
95
96
  "lm-studio": call_lmstudio_api,
96
97
  "minimax": call_minimax_api,
98
+ "mistral": call_mistral_api,
97
99
  "ollama": call_ollama_api,
98
100
  "openai": call_openai_api,
99
101
  "openrouter": call_openrouter_api,
@@ -102,6 +102,7 @@ def generate_with_retries(
102
102
  "groq",
103
103
  "lm-studio",
104
104
  "minimax",
105
+ "mistral",
105
106
  "ollama",
106
107
  "openai",
107
108
  "openrouter",
@@ -154,5 +154,15 @@ cli.add_command(init_cli)
154
154
  cli.add_command(language_cli)
155
155
  cli.add_command(diff_cli)
156
156
 
157
+
158
+ @click.command(context_settings=language_cli.context_settings)
159
+ @click.pass_context
160
+ def lang(ctx):
161
+ """Set the language for commit messages interactively. (Alias for 'language')"""
162
+ ctx.forward(language_cli)
163
+
164
+
165
+ cli.add_command(lang) # Add the lang alias
166
+
157
167
  if __name__ == "__main__":
158
168
  cli()
@@ -214,6 +214,37 @@ class Languages:
214
214
  "fi": "Finnish",
215
215
  }
216
216
 
217
+ # List of languages with display names and English names for CLI selection
218
+ # Format: (display_name, english_name)
219
+ LANGUAGES: list[tuple[str, str]] = [
220
+ ("English", "English"),
221
+ ("简体中文", "Simplified Chinese"),
222
+ ("繁體中文", "Traditional Chinese"),
223
+ ("日本語", "Japanese"),
224
+ ("한국어", "Korean"),
225
+ ("Español", "Spanish"),
226
+ ("Português", "Portuguese"),
227
+ ("Français", "French"),
228
+ ("Deutsch", "German"),
229
+ ("Русский", "Russian"),
230
+ ("हिन्दी", "Hindi"),
231
+ ("Italiano", "Italian"),
232
+ ("Polski", "Polish"),
233
+ ("Türkçe", "Turkish"),
234
+ ("Nederlands", "Dutch"),
235
+ ("Tiếng Việt", "Vietnamese"),
236
+ ("ไทย", "Thai"),
237
+ ("Bahasa Indonesia", "Indonesian"),
238
+ ("Svenska", "Swedish"),
239
+ ("العربية", "Arabic"),
240
+ ("עברית", "Hebrew"),
241
+ ("Ελληνικά", "Greek"),
242
+ ("Dansk", "Danish"),
243
+ ("Norsk", "Norwegian"),
244
+ ("Suomi", "Finnish"),
245
+ ("Custom", "Custom"),
246
+ ]
247
+
217
248
  @staticmethod
218
249
  def resolve_code(language: str) -> str:
219
250
  """Resolve a language code to its full name.
@@ -6,6 +6,8 @@ import click
6
6
  import questionary
7
7
  from dotenv import set_key
8
8
 
9
+ from gac.constants import Languages
10
+
9
11
  GAC_ENV_PATH = Path.home() / ".gac.env"
10
12
 
11
13
 
@@ -33,7 +35,7 @@ def init() -> None:
33
35
 
34
36
  providers = [
35
37
  ("Anthropic", "claude-haiku-4-5"),
36
- ("Cerebras", "qwen-3-coder-480b"),
38
+ ("Cerebras", "zai-glm-4.6"),
37
39
  ("Chutes", "zai-org/GLM-4.6-FP8"),
38
40
  ("Custom (Anthropic)", ""),
39
41
  ("Custom (OpenAI)", ""),
@@ -43,6 +45,7 @@ def init() -> None:
43
45
  ("Groq", "meta-llama/llama-4-maverick-17b-128e-instruct"),
44
46
  ("LM Studio", "gemma3"),
45
47
  ("MiniMax", "MiniMax-M2"),
48
+ ("Mistral", "mistral-small-latest"),
46
49
  ("Ollama", "gemma3"),
47
50
  ("OpenAI", "gpt-4.1-mini"),
48
51
  ("OpenRouter", "openrouter/auto"),
@@ -153,36 +156,7 @@ def init() -> None:
153
156
 
154
157
  # Language selection
155
158
  click.echo("\n")
156
- languages = [
157
- ("English", "English"),
158
- ("简体中文", "Simplified Chinese"),
159
- ("繁體中文", "Traditional Chinese"),
160
- ("日本語", "Japanese"),
161
- ("한국어", "Korean"),
162
- ("Español", "Spanish"),
163
- ("Português", "Portuguese"),
164
- ("Français", "French"),
165
- ("Deutsch", "German"),
166
- ("Русский", "Russian"),
167
- ("हिन्दी", "Hindi"),
168
- ("Italiano", "Italian"),
169
- ("Polski", "Polish"),
170
- ("Türkçe", "Turkish"),
171
- ("Nederlands", "Dutch"),
172
- ("Tiếng Việt", "Vietnamese"),
173
- ("ไทย", "Thai"),
174
- ("Bahasa Indonesia", "Indonesian"),
175
- ("Svenska", "Swedish"),
176
- ("العربية", "Arabic"),
177
- ("עברית", "Hebrew"),
178
- ("Ελληνικά", "Greek"),
179
- ("Dansk", "Danish"),
180
- ("Norsk", "Norwegian"),
181
- ("Suomi", "Finnish"),
182
- ("Custom", "Custom"),
183
- ]
184
-
185
- display_names = [lang[0] for lang in languages]
159
+ display_names = [lang[0] for lang in Languages.LANGUAGES]
186
160
  language_selection = questionary.select(
187
161
  "Select a language for commit messages:", choices=display_names, use_shortcuts=True, use_arrow_keys=True
188
162
  ).ask()
@@ -202,7 +176,7 @@ def init() -> None:
202
176
  language_value = custom_language.strip()
203
177
  else:
204
178
  # Find the English name for the selected language
205
- language_value = next(lang[1] for lang in languages if lang[0] == language_selection)
179
+ language_value = next(lang[1] for lang in Languages.LANGUAGES if lang[0] == language_selection)
206
180
 
207
181
  if language_value:
208
182
  # Ask about prefix translation
@@ -6,6 +6,8 @@ import click
6
6
  import questionary
7
7
  from dotenv import set_key, unset_key
8
8
 
9
+ from gac.constants import Languages
10
+
9
11
  GAC_ENV_PATH = Path.home() / ".gac.env"
10
12
 
11
13
 
@@ -14,38 +16,7 @@ def language() -> None:
14
16
  """Set the language for commit messages interactively."""
15
17
  click.echo("Select a language for your commit messages:\n")
16
18
 
17
- # Languages sorted by programmer population likelihood
18
- # Based on GitHub statistics and global developer demographics
19
- languages = [
20
- ("English", "English"),
21
- ("简体中文", "Simplified Chinese"),
22
- ("繁體中文", "Traditional Chinese"),
23
- ("日本語", "Japanese"),
24
- ("한국어", "Korean"),
25
- ("Español", "Spanish"),
26
- ("Português", "Portuguese"),
27
- ("Français", "French"),
28
- ("Deutsch", "German"),
29
- ("Русский", "Russian"),
30
- ("हिन्दी", "Hindi"),
31
- ("Italiano", "Italian"),
32
- ("Polski", "Polish"),
33
- ("Türkçe", "Turkish"),
34
- ("Nederlands", "Dutch"),
35
- ("Tiếng Việt", "Vietnamese"),
36
- ("ไทย", "Thai"),
37
- ("Bahasa Indonesia", "Indonesian"),
38
- ("Svenska", "Swedish"),
39
- ("العربية", "Arabic"),
40
- ("עברית", "Hebrew"),
41
- ("Ελληνικά", "Greek"),
42
- ("Dansk", "Danish"),
43
- ("Norsk", "Norwegian"),
44
- ("Suomi", "Finnish"),
45
- ("Custom", "Custom"),
46
- ]
47
-
48
- display_names = [lang[0] for lang in languages]
19
+ display_names = [lang[0] for lang in Languages.LANGUAGES]
49
20
  selection = questionary.select(
50
21
  "Choose your language:", choices=display_names, use_shortcuts=True, use_arrow_keys=True
51
22
  ).ask()
@@ -78,7 +49,7 @@ def language() -> None:
78
49
  language_value = custom_language.strip()
79
50
  else:
80
51
  # Find the English name for the selected language
81
- language_value = next(lang[1] for lang in languages if lang[0] == selection)
52
+ language_value = next(lang[1] for lang in Languages.LANGUAGES if lang[0] == selection)
82
53
 
83
54
  # Ask about prefix translation
84
55
  click.echo() # Blank line for spacing
@@ -11,6 +11,7 @@ from .gemini import call_gemini_api
11
11
  from .groq import call_groq_api
12
12
  from .lmstudio import call_lmstudio_api
13
13
  from .minimax import call_minimax_api
14
+ from .mistral import call_mistral_api
14
15
  from .ollama import call_ollama_api
15
16
  from .openai import call_openai_api
16
17
  from .openrouter import call_openrouter_api
@@ -31,6 +32,7 @@ __all__ = [
31
32
  "call_groq_api",
32
33
  "call_lmstudio_api",
33
34
  "call_minimax_api",
35
+ "call_mistral_api",
34
36
  "call_ollama_api",
35
37
  "call_openai_api",
36
38
  "call_openrouter_api",
@@ -0,0 +1,38 @@
1
+ """Mistral API provider for gac."""
2
+
3
+ import os
4
+
5
+ import httpx
6
+
7
+ from gac.errors import AIError
8
+
9
+
10
+ def call_mistral_api(model: str, messages: list[dict], temperature: float, max_tokens: int) -> str:
11
+ """Call Mistral API directly."""
12
+ api_key = os.getenv("MISTRAL_API_KEY")
13
+ if not api_key:
14
+ raise AIError.authentication_error("MISTRAL_API_KEY not found in environment variables")
15
+
16
+ url = "https://api.mistral.ai/v1/chat/completions"
17
+ headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
18
+
19
+ data = {"model": model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens}
20
+
21
+ try:
22
+ response = httpx.post(url, headers=headers, json=data, timeout=120)
23
+ response.raise_for_status()
24
+ response_data = response.json()
25
+ content = response_data["choices"][0]["message"]["content"]
26
+ if content is None:
27
+ raise AIError.model_error("Mistral API returned null content")
28
+ if content == "":
29
+ raise AIError.model_error("Mistral API returned empty content")
30
+ return content
31
+ except httpx.HTTPStatusError as e:
32
+ if e.response.status_code == 429:
33
+ raise AIError.rate_limit_error(f"Mistral API rate limit exceeded: {e.response.text}") from e
34
+ raise AIError.model_error(f"Mistral API error: {e.response.status_code} - {e.response.text}") from e
35
+ except httpx.TimeoutException as e:
36
+ raise AIError.timeout_error(f"Mistral API request timed out: {str(e)}") from e
37
+ except Exception as e:
38
+ raise AIError.model_error(f"Error calling Mistral API: {str(e)}") from e
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
File without changes
File without changes