gac 1.15.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-1.15.0 → gac-2.1.0}/PKG-INFO +23 -15
  2. {gac-1.15.0 → gac-2.1.0}/README.md +22 -14
  3. {gac-1.15.0 → gac-2.1.0}/src/gac/__version__.py +1 -1
  4. {gac-1.15.0 → gac-2.1.0}/src/gac/ai.py +2 -0
  5. {gac-1.15.0 → gac-2.1.0}/src/gac/ai_utils.py +1 -0
  6. {gac-1.15.0 → gac-2.1.0}/src/gac/cli.py +10 -0
  7. {gac-1.15.0 → gac-2.1.0}/src/gac/constants.py +31 -0
  8. {gac-1.15.0 → gac-2.1.0}/src/gac/init_cli.py +6 -32
  9. {gac-1.15.0 → gac-2.1.0}/src/gac/language_cli.py +4 -33
  10. {gac-1.15.0 → gac-2.1.0}/src/gac/main.py +16 -20
  11. {gac-1.15.0 → gac-2.1.0}/src/gac/prompt.py +3 -3
  12. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/__init__.py +2 -0
  13. gac-2.1.0/src/gac/providers/mistral.py +38 -0
  14. {gac-1.15.0 → gac-2.1.0}/.gitignore +0 -0
  15. {gac-1.15.0 → gac-2.1.0}/LICENSE +0 -0
  16. {gac-1.15.0 → gac-2.1.0}/pyproject.toml +0 -0
  17. {gac-1.15.0 → gac-2.1.0}/src/gac/__init__.py +0 -0
  18. {gac-1.15.0 → gac-2.1.0}/src/gac/config.py +0 -0
  19. {gac-1.15.0 → gac-2.1.0}/src/gac/config_cli.py +0 -0
  20. {gac-1.15.0 → gac-2.1.0}/src/gac/diff_cli.py +0 -0
  21. {gac-1.15.0 → gac-2.1.0}/src/gac/errors.py +0 -0
  22. {gac-1.15.0 → gac-2.1.0}/src/gac/git.py +0 -0
  23. {gac-1.15.0 → gac-2.1.0}/src/gac/preprocess.py +0 -0
  24. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/anthropic.py +0 -0
  25. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/cerebras.py +0 -0
  26. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/chutes.py +0 -0
  27. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/custom_anthropic.py +0 -0
  28. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/custom_openai.py +0 -0
  29. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/deepseek.py +0 -0
  30. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/fireworks.py +0 -0
  31. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/gemini.py +0 -0
  32. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/groq.py +0 -0
  33. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/lmstudio.py +0 -0
  34. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/minimax.py +0 -0
  35. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/ollama.py +0 -0
  36. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/openai.py +0 -0
  37. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/openrouter.py +0 -0
  38. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/streamlake.py +0 -0
  39. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/synthetic.py +0 -0
  40. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/together.py +0 -0
  41. {gac-1.15.0 → gac-2.1.0}/src/gac/providers/zai.py +0 -0
  42. {gac-1.15.0 → gac-2.1.0}/src/gac/security.py +0 -0
  43. {gac-1.15.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: 1.15.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
 
@@ -117,7 +123,7 @@ gac
117
123
 
118
124
  ### 💻 **Developer Experience**
119
125
 
120
- - **Interactive feedback**: Regenerate messages with specific requests like `r "make it shorter"` or `r "focus on the bug fix"`
126
+ - **Interactive feedback**: Type `r` to reroll, or directly type your feedback like `make it shorter` or `focus on the bug fix`
121
127
  - **One-command workflows**: Complete workflows with flags like `gac -ayp` (stage all, auto-confirm, push)
122
128
  - **Git integration**: Respects pre-commit and lefthook hooks, running them before expensive LLM operations
123
129
 
@@ -140,7 +146,7 @@ git add .
140
146
  # Generate and commit with LLM
141
147
  gac
142
148
 
143
- # Review → y (commit) | n (cancel) | r (reroll)
149
+ # Review → y (commit) | n (cancel) | r (reroll) | or type feedback
144
150
  ```
145
151
 
146
152
  ### Common Commands
@@ -175,18 +181,20 @@ gac --show-prompt
175
181
  gac --skip-secret-scan
176
182
  ```
177
183
 
178
- ### Interactive Reroll System
184
+ ### Interactive Feedback System
179
185
 
180
- Not happy with the result? Use the reroll feature for intelligent regeneration:
186
+ Not happy with the result? You have two options:
181
187
 
182
188
  ```bash
183
- # Simple reroll
189
+ # Simple reroll (no feedback)
184
190
  r
185
191
 
186
- # With specific feedback
187
- r make it shorter and focus on the performance improvement
188
- r use conventional commit format with scope
189
- r explain the security implications
192
+ # Or just type your feedback directly!
193
+ make it shorter and focus on the performance improvement
194
+ use conventional commit format with scope
195
+ explain the security implications
196
+
197
+ # Press Enter on empty input to see the prompt again
190
198
  ```
191
199
 
192
200
  ---
@@ -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
 
@@ -76,7 +82,7 @@ gac
76
82
 
77
83
  ### 💻 **Developer Experience**
78
84
 
79
- - **Interactive feedback**: Regenerate messages with specific requests like `r "make it shorter"` or `r "focus on the bug fix"`
85
+ - **Interactive feedback**: Type `r` to reroll, or directly type your feedback like `make it shorter` or `focus on the bug fix`
80
86
  - **One-command workflows**: Complete workflows with flags like `gac -ayp` (stage all, auto-confirm, push)
81
87
  - **Git integration**: Respects pre-commit and lefthook hooks, running them before expensive LLM operations
82
88
 
@@ -99,7 +105,7 @@ git add .
99
105
  # Generate and commit with LLM
100
106
  gac
101
107
 
102
- # Review → y (commit) | n (cancel) | r (reroll)
108
+ # Review → y (commit) | n (cancel) | r (reroll) | or type feedback
103
109
  ```
104
110
 
105
111
  ### Common Commands
@@ -134,18 +140,20 @@ gac --show-prompt
134
140
  gac --skip-secret-scan
135
141
  ```
136
142
 
137
- ### Interactive Reroll System
143
+ ### Interactive Feedback System
138
144
 
139
- Not happy with the result? Use the reroll feature for intelligent regeneration:
145
+ Not happy with the result? You have two options:
140
146
 
141
147
  ```bash
142
- # Simple reroll
148
+ # Simple reroll (no feedback)
143
149
  r
144
150
 
145
- # With specific feedback
146
- r make it shorter and focus on the performance improvement
147
- r use conventional commit format with scope
148
- r explain the security implications
151
+ # Or just type your feedback directly!
152
+ make it shorter and focus on the performance improvement
153
+ use conventional commit format with scope
154
+ explain the security implications
155
+
156
+ # Press Enter on empty input to see the prompt again
149
157
  ```
150
158
 
151
159
  ---
@@ -1,3 +1,3 @@
1
1
  """Version information for gac package."""
2
2
 
3
- __version__ = "1.15.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
@@ -275,10 +275,11 @@ def main(
275
275
  )
276
276
 
277
277
  if require_confirmation:
278
- # Custom prompt that accepts y/n/r or "r <feedback (optional)>"
279
278
  while True:
280
279
  response = click.prompt(
281
- "Proceed with commit above? [y/n/r <feedback>]", type=str, show_default=False
280
+ "Proceed with commit above? [y/n/r/<feedback>]",
281
+ type=str,
282
+ show_default=False,
282
283
  ).strip()
283
284
  response_lower = response.lower()
284
285
 
@@ -287,27 +288,22 @@ def main(
287
288
  if response_lower in ["n", "no"]:
288
289
  console.print("[yellow]Prompt not accepted. Exiting...[/yellow]")
289
290
  sys.exit(0)
290
- if response_lower == "r" or response_lower == "reroll" or response_lower.startswith("r "):
291
- if response_lower == "r" or response_lower == "reroll":
292
- feedback_message = (
293
- "Please provide an alternative commit message using the same repository context."
294
- )
295
- console.print("[cyan]Regenerating commit message...[/cyan]")
296
- else:
297
- reroll_feedback = response[2:].strip()
298
- feedback_message = (
299
- f"Please revise the commit message based on this feedback: {reroll_feedback}"
300
- )
301
- console.print(f"[cyan]Regenerating commit message with feedback: {reroll_feedback}[/cyan]")
302
-
291
+ if response == "":
292
+ continue
293
+ if response_lower in ["r", "reroll"]:
294
+ feedback_message = (
295
+ "Please provide an alternative commit message using the same repository context."
296
+ )
297
+ console.print("[cyan]Regenerating commit message...[/cyan]")
303
298
  conversation_messages.append({"role": "user", "content": feedback_message})
304
-
305
- console.print() # Add blank line for readability
299
+ console.print()
306
300
  break
307
301
 
308
- console.print(
309
- "[red]Invalid response. Please enter y (yes), n (no), r (reroll), or r <feedback>.[/red]"
310
- )
302
+ feedback_message = f"Please revise the commit message based on this feedback: {response}"
303
+ console.print(f"[cyan]Regenerating commit message with feedback: {response}[/cyan]")
304
+ conversation_messages.append({"role": "user", "content": feedback_message})
305
+ console.print()
306
+ break
311
307
 
312
308
  if response_lower in ["y", "yes"]:
313
309
  break
@@ -39,12 +39,12 @@ When changes span multiple areas:
39
39
 
40
40
  <format>
41
41
  <one_liner>
42
- Create a single-line commit message (50-72 characters if possible).
42
+ Create a single-line commit message.
43
43
  Your message should be clear, concise, and descriptive of the core change.
44
44
  Use present tense ("Add feature" not "Added feature").
45
45
  </one_liner><multi_line>
46
46
  Create a commit message with:
47
- - First line: A concise summary (50-72 characters) that could stand alone
47
+ - First line: A concise summary that could stand alone
48
48
  - Blank line after the summary
49
49
  - Detailed body with multiple bullet points explaining the key changes
50
50
  - Focus on WHY changes were made, not just WHAT was changed
@@ -55,7 +55,7 @@ When changes span multiple areas:
55
55
 
56
56
  Your commit message MUST follow this structure:
57
57
 
58
- Line 1: A concise summary (up to ~72 characters) with conventional commit prefix
58
+ Line 1: A concise summary (that could stand alone) with conventional commit prefix
59
59
  Line 2: BLANK LINE (required)
60
60
  Lines 3+: Detailed multi-paragraph body with the following sections:
61
61
 
@@ -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