gac 2.0.0__py3-none-any.whl → 2.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.

Potentially problematic release.


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

gac/__version__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Version information for gac package."""
2
2
 
3
- __version__ = "2.0.0"
3
+ __version__ = "2.1.0"
gac/ai.py CHANGED
@@ -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,
gac/ai_utils.py CHANGED
@@ -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",
gac/cli.py CHANGED
@@ -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()
gac/constants.py CHANGED
@@ -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.
gac/init_cli.py CHANGED
@@ -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
gac/language_cli.py CHANGED
@@ -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
gac/providers/__init__.py CHANGED
@@ -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
@@ -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
@@ -1,22 +1,22 @@
1
1
  gac/__init__.py,sha256=z9yGInqtycFIT3g1ca24r-A3699hKVaRqGUI79wsmMc,415
2
- gac/__version__.py,sha256=sXY5h9N-vjDIgAsvaFio9v3KucauBzZ9AFdd_Oe8M28,66
3
- gac/ai.py,sha256=fg642la4yMecOwfZHQ7Ixl6z-5_qj9Q1SxwVMnPDCcY,4244
4
- gac/ai_utils.py,sha256=EDkw0nnwnV5Ba2CLEo2HC15-L5BZtGJATin5Az0ZHkg,7426
5
- gac/cli.py,sha256=TV1IWwcRmEfbqXLlfGmTms5NCEqaJXUGIdFxmOg0tC0,5546
2
+ gac/__version__.py,sha256=bTn7Tm6xpc6WrZ2ANt-S3NGI6Rng6eG5VxYTYs-UWqM,66
3
+ gac/ai.py,sha256=iBHeLsqe6iyFj86wbvEosyy4vkjAN1BlLQeqtb_rfmo,4303
4
+ gac/ai_utils.py,sha256=094ujZVlbDnHM3HPxiBSCbGi_5MD6bOKCj2SjKVDDK0,7445
5
+ gac/cli.py,sha256=SOrSfrlku99O7O8zev5hRVmADAmJ7AIkM7Z0dquuCbQ,5807
6
6
  gac/config.py,sha256=O9n09-sFOqlkf47vieEP7fI5I7uhu1cXn9PUZ5yiYkw,1974
7
7
  gac/config_cli.py,sha256=v9nFHZO1RvK9fzHyuUS6SG-BCLHMsdOMDwWamBhVVh4,1608
8
- gac/constants.py,sha256=-VYqL1M99RAmEwQHTe6lMYVkPHSSYQJUvzYvBp8Bmck,8494
8
+ gac/constants.py,sha256=Spuqa1ntae0CtNGu-Emxzc_Zz1CipikO3h5BWcUlUW0,9586
9
9
  gac/diff_cli.py,sha256=wnVQ9OFGnM0d2Pj9WVjWbo0jxqIuRHVAwmb8wU9Pa3E,5676
10
10
  gac/errors.py,sha256=ysDIVRCd0YQVTOW3Q6YzdolxCdtkoQCAFf3_jrqbjUY,7916
11
11
  gac/git.py,sha256=g6tvph50zV-wrTWrxARYXEpl0NeI8-ffFwHoqhp3fSE,8033
12
- gac/init_cli.py,sha256=wyyPNjO47IcwFrO-jrqJ5rs1SsKdDJnhuRO83heh1tw,9700
13
- gac/language_cli.py,sha256=xmmIonIhOR83VtRFj5Dy8JDtpa5qCNbNxSEZt-1T0a8,4040
12
+ gac/init_cli.py,sha256=KvFOvjjyMpdJ3MhJFvXSuYjdfulPA6hCP11YXwjHrqw,8849
13
+ gac/language_cli.py,sha256=J4xZNNrMvHamsjK4TCsOVj0lrjYDtLMuHlnTqN0-N_w,3024
14
14
  gac/main.py,sha256=LJhONOP_w09TE7HOd4T_NiAzmiqemY2N7EZ6biqwVQE,15221
15
15
  gac/preprocess.py,sha256=hk2p2X4-xVDvuy-T1VMzMa9k5fTUbhlWDyw89DCf81Q,15379
16
16
  gac/prompt.py,sha256=HLvsW3YQLdTfw2N9UgjZ0jWckUc1x76V7Kcqjcl8Fsk,28633
17
17
  gac/security.py,sha256=15Yp6YR8QC4eECJi1BUCkMteh_veZXUbLL6W8qGcDm4,9920
18
18
  gac/utils.py,sha256=nV42-brIHW_fBg7x855GM8nRrqEBbRzTSweg-GTyGE8,3971
19
- gac/providers/__init__.py,sha256=3WTzh3ngAdvR40eezpMMFD7Zibb-LxexDYUcSm4axQI,1305
19
+ gac/providers/__init__.py,sha256=pT1xcKoZkPm6BWaxcAQ299-Ca5zZ31kf4DeQYAim9Tw,1367
20
20
  gac/providers/anthropic.py,sha256=VK5d1s1PmBNDwh_x7illQ2CIZIHNIYU28btVfizwQPs,2036
21
21
  gac/providers/cerebras.py,sha256=Ik8lhlsliGJVkgDgqlThfpra9tqbdYQZkaC4eNxRd9w,1648
22
22
  gac/providers/chutes.py,sha256=cclJOLuGVIiiaF-9Bs1kH6SSOhEmduGB2zZ86KIaXKw,2617
@@ -28,6 +28,7 @@ gac/providers/gemini.py,sha256=kl9WKdPm_ANYk0hsrUyMdACzR0cm8Eui9M1IwObYW-4,3348
28
28
  gac/providers/groq.py,sha256=9v2fAjDa_iRNHFptiUBN8Vt7ZDKkW_JOmIBeYvycD1M,2806
29
29
  gac/providers/lmstudio.py,sha256=R82-f0tWdFfGQxLT6o3Q2tfvYguF7ESUg9DEUHNyrDk,2146
30
30
  gac/providers/minimax.py,sha256=oI5rEVlkcYenNUNH53zS00X8NqpcZ1gMsTGzQCsmes4,1630
31
+ gac/providers/mistral.py,sha256=b2Du1nJutKjmJXmsXz4Ne43Yn52OSS0q6BKGoZnfH8Q,1630
31
32
  gac/providers/ollama.py,sha256=hPkagbhEiAoH9RTET4EQe9-lTL0YmMRCbQ5dVbRQw6Q,2095
32
33
  gac/providers/openai.py,sha256=iHVD6bHf57W-QmW7u1Ee5vOpev7XZ-K75NcolLfebOk,1630
33
34
  gac/providers/openrouter.py,sha256=H3ce8JcRUYq1I30lOjGESdX7jfoPkW3mKAYnc2aYfBw,2204
@@ -35,8 +36,8 @@ gac/providers/streamlake.py,sha256=KAA2ZnpuEI5imzvdWVWUhEBHSP0BMnprKXte6CbwBWY,2
35
36
  gac/providers/synthetic.py,sha256=sRMIJTS9LpcXd9A7qp_ZjZxdqtTKRn9fl1W4YwJZP4c,1855
36
37
  gac/providers/together.py,sha256=1bUIVHfYzcEDw4hQPE8qV6hjc2JNHPv_khVgpk2IJxI,1667
37
38
  gac/providers/zai.py,sha256=kywhhrCfPBu0rElZyb-iENxQxxpVGykvePuL4xrXlaU,2739
38
- gac-2.0.0.dist-info/METADATA,sha256=X4MCo_VM2qkYzwREOCmOZSpV5YC_NZbTH9o4yHOyuHg,8909
39
- gac-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
40
- gac-2.0.0.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
41
- gac-2.0.0.dist-info/licenses/LICENSE,sha256=vOab37NouL1PNs5BswnPayrMCqaN2sqLfMQfqPDrpZg,1103
42
- gac-2.0.0.dist-info/RECORD,,
39
+ gac-2.1.0.dist-info/METADATA,sha256=VYrOHxHKI_6jbenHBBKt3F8IfBNf-n4Vlnf_LUYBuGM,8958
40
+ gac-2.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
+ gac-2.1.0.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
42
+ gac-2.1.0.dist-info/licenses/LICENSE,sha256=vOab37NouL1PNs5BswnPayrMCqaN2sqLfMQfqPDrpZg,1103
43
+ gac-2.1.0.dist-info/RECORD,,
File without changes