gac 1.9.4__py3-none-any.whl → 1.10.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__ = "1.9.4"
3
+ __version__ = "1.10.0"
gac/ai.py CHANGED
@@ -13,6 +13,7 @@ from gac.providers import (
13
13
  call_anthropic_api,
14
14
  call_cerebras_api,
15
15
  call_chutes_api,
16
+ call_fireworks_api,
16
17
  call_gemini_api,
17
18
  call_groq_api,
18
19
  call_lmstudio_api,
@@ -30,7 +31,7 @@ logger = logging.getLogger(__name__)
30
31
 
31
32
  def generate_commit_message(
32
33
  model: str,
33
- prompt: str | tuple[str, str],
34
+ prompt: str | tuple[str, str] | list[dict[str, str]],
34
35
  temperature: float = EnvDefaults.TEMPERATURE,
35
36
  max_tokens: int = EnvDefaults.MAX_OUTPUT_TOKENS,
36
37
  max_retries: int = EnvDefaults.MAX_RETRIES,
@@ -59,18 +60,28 @@ def generate_commit_message(
59
60
  'docs: Update README with installation instructions'
60
61
  """
61
62
  # Handle both old (string) and new (tuple) prompt formats
62
- if isinstance(prompt, tuple):
63
+ if isinstance(prompt, list):
64
+ messages = [{**msg} for msg in prompt]
65
+ elif isinstance(prompt, tuple):
63
66
  system_prompt, user_prompt = prompt
67
+ messages = [
68
+ {"role": "system", "content": system_prompt or ""},
69
+ {"role": "user", "content": user_prompt},
70
+ ]
64
71
  else:
65
- # Backward compatibility: treat string as user prompt with no system prompt
66
- system_prompt = ""
67
- user_prompt = prompt
72
+ # Backward compatibility: treat string as user prompt with empty system prompt
73
+ user_prompt = str(prompt)
74
+ messages = [
75
+ {"role": "system", "content": ""},
76
+ {"role": "user", "content": user_prompt},
77
+ ]
68
78
 
69
79
  # Provider functions mapping
70
80
  provider_funcs = {
71
81
  "anthropic": call_anthropic_api,
72
82
  "cerebras": call_cerebras_api,
73
83
  "chutes": call_chutes_api,
84
+ "fireworks": call_fireworks_api,
74
85
  "gemini": call_gemini_api,
75
86
  "groq": call_groq_api,
76
87
  "lm-studio": call_lmstudio_api,
@@ -88,8 +99,7 @@ def generate_commit_message(
88
99
  return generate_with_retries(
89
100
  provider_funcs=provider_funcs,
90
101
  model=model,
91
- system_prompt=system_prompt,
92
- user_prompt=user_prompt,
102
+ messages=messages,
93
103
  temperature=temperature,
94
104
  max_tokens=max_tokens,
95
105
  max_retries=max_retries,
gac/ai_utils.py CHANGED
@@ -78,8 +78,7 @@ def _classify_error(error_str: str) -> str:
78
78
  def generate_with_retries(
79
79
  provider_funcs: dict,
80
80
  model: str,
81
- system_prompt: str,
82
- user_prompt: str,
81
+ messages: list[dict[str, str]],
83
82
  temperature: float,
84
83
  max_tokens: int,
85
84
  max_retries: int,
@@ -97,6 +96,7 @@ def generate_with_retries(
97
96
  "anthropic",
98
97
  "cerebras",
99
98
  "chutes",
99
+ "fireworks",
100
100
  "gemini",
101
101
  "groq",
102
102
  "lm-studio",
@@ -111,10 +111,8 @@ def generate_with_retries(
111
111
  if provider not in supported_providers:
112
112
  raise AIError.model_error(f"Unsupported provider: {provider}. Supported providers: {supported_providers}")
113
113
 
114
- messages = [
115
- {"role": "system", "content": system_prompt},
116
- {"role": "user", "content": user_prompt},
117
- ]
114
+ if not messages:
115
+ raise AIError.model_error("No messages provided for AI generation")
118
116
 
119
117
  # Set up spinner
120
118
  if quiet:
gac/init_cli.py CHANGED
@@ -35,6 +35,7 @@ def init() -> None:
35
35
  ("Anthropic", "claude-haiku-4-5"),
36
36
  ("Cerebras", "qwen-3-coder-480b"),
37
37
  ("Chutes", "zai-org/GLM-4.6-FP8"),
38
+ ("Fireworks", "accounts/fireworks/models/gpt-oss-20b"),
38
39
  ("Gemini", "gemini-2.5-flash"),
39
40
  ("Groq", "meta-llama/llama-4-maverick-17b-128e-instruct"),
40
41
  ("LM Studio", "gemma3"),
gac/main.py CHANGED
@@ -202,39 +202,49 @@ def main(
202
202
  )
203
203
  )
204
204
 
205
+ conversation_messages: list[dict[str, str]] = []
206
+ if system_prompt:
207
+ conversation_messages.append({"role": "system", "content": system_prompt})
208
+ conversation_messages.append({"role": "user", "content": user_prompt})
209
+
205
210
  try:
206
- # Count tokens for both prompts
207
- prompt_tokens = count_tokens(system_prompt, model) + count_tokens(user_prompt, model)
208
-
209
- warning_limit_val = config.get("warning_limit_tokens", EnvDefaults.WARNING_LIMIT_TOKENS)
210
- assert warning_limit_val is not None
211
- warning_limit = int(warning_limit_val)
212
- if warning_limit and prompt_tokens > warning_limit:
213
- console.print(
214
- f"[yellow]⚠️ WARNING: Prompt contains {prompt_tokens} tokens, which exceeds the warning limit of "
215
- f"{warning_limit} tokens.[/yellow]"
211
+ first_iteration = True
212
+
213
+ while True:
214
+ prompt_tokens = count_tokens(conversation_messages, model)
215
+
216
+ if first_iteration:
217
+ warning_limit_val = config.get("warning_limit_tokens", EnvDefaults.WARNING_LIMIT_TOKENS)
218
+ assert warning_limit_val is not None
219
+ warning_limit = int(warning_limit_val)
220
+ if warning_limit and prompt_tokens > warning_limit:
221
+ console.print(
222
+ f"[yellow]⚠️ WARNING: Prompt contains {prompt_tokens} tokens, which exceeds the warning limit of "
223
+ f"{warning_limit} tokens.[/yellow]"
224
+ )
225
+ if require_confirmation:
226
+ proceed = click.confirm("Do you want to continue anyway?", default=True)
227
+ if not proceed:
228
+ console.print("[yellow]Aborted due to token limit.[/yellow]")
229
+ sys.exit(0)
230
+
231
+ first_iteration = False
232
+
233
+ raw_commit_message = generate_commit_message(
234
+ model=model,
235
+ prompt=conversation_messages,
236
+ temperature=temperature,
237
+ max_tokens=max_output_tokens,
238
+ max_retries=max_retries,
239
+ quiet=quiet,
216
240
  )
217
- if require_confirmation:
218
- proceed = click.confirm("Do you want to continue anyway?", default=True)
219
- if not proceed:
220
- console.print("[yellow]Aborted due to token limit.[/yellow]")
221
- sys.exit(0)
241
+ commit_message = clean_commit_message(raw_commit_message)
222
242
 
223
- commit_message = generate_commit_message(
224
- model=model,
225
- prompt=(system_prompt, user_prompt),
226
- temperature=temperature,
227
- max_tokens=max_output_tokens,
228
- max_retries=max_retries,
229
- quiet=quiet,
230
- )
231
- commit_message = clean_commit_message(commit_message)
243
+ logger.info("Generated commit message:")
244
+ logger.info(commit_message)
232
245
 
233
- logger.info("Generated commit message:")
234
- logger.info(commit_message)
246
+ conversation_messages.append({"role": "assistant", "content": commit_message})
235
247
 
236
- # Reroll loop
237
- while True:
238
248
  console.print("[bold green]Generated commit message:[/bold green]")
239
249
  console.print(Panel(commit_message, title="Commit Message", border_style="cyan"))
240
250
 
@@ -252,71 +262,38 @@ def main(
252
262
  response = click.prompt(
253
263
  "Proceed with commit above? [y/n/r <feedback>]", type=str, show_default=False
254
264
  ).strip()
265
+ response_lower = response.lower()
255
266
 
256
- if response.lower() in ["y", "yes"]:
257
- break # Exit both loops and proceed with commit
258
- elif response.lower() in ["n", "no"]:
267
+ if response_lower in ["y", "yes"]:
268
+ break
269
+ if response_lower in ["n", "no"]:
259
270
  console.print("[yellow]Prompt not accepted. Exiting...[/yellow]")
260
271
  sys.exit(0)
261
- elif response.lower() == "r" or response.lower().startswith("r ") or response.lower() == "reroll":
262
- # Parse the reroll command for optional feedback
263
- if response.lower() == "r" or response.lower() == "reroll":
264
- # Simple reroll without feedback
265
- reroll_feedback = ""
272
+ if response_lower == "r" or response_lower == "reroll" or response_lower.startswith("r "):
273
+ if response_lower == "r" or response_lower == "reroll":
274
+ feedback_message = (
275
+ "Please provide an alternative commit message using the same repository context."
276
+ )
266
277
  console.print("[cyan]Regenerating commit message...[/cyan]")
267
278
  else:
268
- # Extract feedback from "r <feedback>"
269
- reroll_feedback = response[2:].strip() # Remove "r " prefix
279
+ reroll_feedback = response[2:].strip()
280
+ feedback_message = (
281
+ f"Please revise the commit message based on this feedback: {reroll_feedback}"
282
+ )
270
283
  console.print(f"[cyan]Regenerating commit message with feedback: {reroll_feedback}[/cyan]")
271
284
 
272
- # Combine hints if reroll feedback provided
273
- combined_hint = hint
274
- if reroll_feedback:
275
- # Create conversational prompt with previous attempt and feedback
276
- conversational_hint = f"Previous attempt: '{commit_message}'. User feedback: {reroll_feedback}. Please revise accordingly."
277
-
278
- if hint:
279
- combined_hint = f"{hint}. {conversational_hint}"
280
- else:
281
- combined_hint = conversational_hint
282
-
283
- # Regenerate prompt with conversational feedback
284
- reroll_system_prompt, reroll_user_prompt = build_prompt(
285
- status=status,
286
- processed_diff=processed_diff,
287
- diff_stat=diff_stat,
288
- one_liner=one_liner,
289
- hint=combined_hint,
290
- infer_scope=infer_scope,
291
- verbose=verbose,
292
- )
293
- else:
294
- # No hint given, just reroll with same prompts
295
- reroll_system_prompt, reroll_user_prompt = system_prompt, user_prompt
285
+ conversation_messages.append({"role": "user", "content": feedback_message})
296
286
 
297
287
  console.print() # Add blank line for readability
288
+ break
289
+
290
+ console.print(
291
+ "[red]Invalid response. Please enter y (yes), n (no), r (reroll), or r <feedback>.[/red]"
292
+ )
298
293
 
299
- # Generate new message
300
- commit_message = generate_commit_message(
301
- model=model,
302
- prompt=(reroll_system_prompt, reroll_user_prompt),
303
- temperature=temperature,
304
- max_tokens=max_output_tokens,
305
- max_retries=max_retries,
306
- quiet=quiet,
307
- )
308
- commit_message = clean_commit_message(commit_message)
309
- break # Exit inner loop, continue outer loop
310
- else:
311
- console.print(
312
- "[red]Invalid response. Please enter y (yes), n (no), r (reroll), or r <feedback>.[/red]"
313
- )
314
-
315
- # If we got here with 'y', break the outer loop
316
- if response.lower() in ["y", "yes"]:
294
+ if response_lower in ["y", "yes"]:
317
295
  break
318
296
  else:
319
- # No confirmation required, exit loop
320
297
  break
321
298
 
322
299
  if dry_run:
gac/providers/__init__.py CHANGED
@@ -3,6 +3,7 @@
3
3
  from .anthropic import call_anthropic_api
4
4
  from .cerebras import call_cerebras_api
5
5
  from .chutes import call_chutes_api
6
+ from .fireworks import call_fireworks_api
6
7
  from .gemini import call_gemini_api
7
8
  from .groq import call_groq_api
8
9
  from .lmstudio import call_lmstudio_api
@@ -17,6 +18,7 @@ __all__ = [
17
18
  "call_anthropic_api",
18
19
  "call_cerebras_api",
19
20
  "call_chutes_api",
21
+ "call_fireworks_api",
20
22
  "call_gemini_api",
21
23
  "call_groq_api",
22
24
  "call_lmstudio_api",
@@ -0,0 +1,38 @@
1
+ """Fireworks AI API provider for gac."""
2
+
3
+ import os
4
+
5
+ import httpx
6
+
7
+ from gac.errors import AIError
8
+
9
+
10
+ def call_fireworks_api(model: str, messages: list[dict], temperature: float, max_tokens: int) -> str:
11
+ """Call Fireworks AI API directly."""
12
+ api_key = os.getenv("FIREWORKS_API_KEY")
13
+ if not api_key:
14
+ raise AIError.authentication_error("FIREWORKS_API_KEY not found in environment variables")
15
+
16
+ url = "https://api.fireworks.ai/inference/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("Fireworks AI API returned null content")
28
+ if content == "":
29
+ raise AIError.model_error("Fireworks AI 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"Fireworks AI API rate limit exceeded: {e.response.text}") from e
34
+ raise AIError.model_error(f"Fireworks AI API error: {e.response.status_code} - {e.response.text}") from e
35
+ except httpx.TimeoutException as e:
36
+ raise AIError.timeout_error(f"Fireworks AI API request timed out: {str(e)}") from e
37
+ except Exception as e:
38
+ raise AIError.model_error(f"Error calling Fireworks AI API: {str(e)}") from e
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: gac
3
+ Version: 1.10.0
4
+ Summary: LLM-powered Git commit message generator with multi-provider support
5
+ Project-URL: Homepage, https://github.com/cellwebb/gac
6
+ Project-URL: Documentation, https://github.com/cellwebb/gac#readme
7
+ Project-URL: Repository, https://github.com/cellwebb/gac.git
8
+ Project-URL: Issues, https://github.com/cellwebb/gac/issues
9
+ Project-URL: Changelog, https://github.com/cellwebb/gac/blob/main/CHANGELOG.md
10
+ Project-URL: Source, https://github.com/cellwebb/gac
11
+ Author-email: cellwebb <cellwebb@users.noreply.github.com>
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: Implementation :: CPython
22
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: click>=8.3.0
25
+ Requires-Dist: halo
26
+ Requires-Dist: httpcore>=1.0.9
27
+ Requires-Dist: httpx>=0.28.0
28
+ Requires-Dist: pydantic>=2.12.0
29
+ Requires-Dist: python-dotenv>=1.1.1
30
+ Requires-Dist: questionary
31
+ Requires-Dist: rich>=14.1.0
32
+ Requires-Dist: tiktoken>=0.12.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: build; extra == 'dev'
35
+ Requires-Dist: codecov; extra == 'dev'
36
+ Requires-Dist: pre-commit; extra == 'dev'
37
+ Requires-Dist: pytest; extra == 'dev'
38
+ Requires-Dist: pytest-cov; extra == 'dev'
39
+ Requires-Dist: ruff; extra == 'dev'
40
+ Requires-Dist: twine; extra == 'dev'
41
+ Description-Content-Type: text/markdown
42
+
43
+ <!-- markdownlint-disable MD013 -->
44
+
45
+ # 🚀 Git Auto Commit (gac)
46
+
47
+ [![PyPI version](https://img.shields.io/pypi/v/gac.svg)](https://pypi.org/project/gac/)
48
+ [![Python](https://img.shields.io/badge/python-3.10%20|%203.11%20|%203.12%20|%203.13%20|%203.14-blue.svg)](https://www.python.org/downloads/)
49
+ [![Build Status](https://github.com/cellwebb/gac/actions/workflows/ci.yml/badge.svg)](https://github.com/cellwebb/gac/actions)
50
+ [![codecov](https://codecov.io/gh/cellwebb/gac/branch/main/graph/badge.svg)](https://app.codecov.io/gh/cellwebb/gac)
51
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
52
+ [![mypy](https://img.shields.io/badge/mypy-checked-blue.svg)](https://mypy-lang.org/)
53
+ [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](docs/CONTRIBUTING.md)
54
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
55
+
56
+ **LLM-powered commit messages that understand your code.**
57
+
58
+ **Tired of writing commit messages?** Replace `git commit -m "..."` with `gac` for contextual, well-formatted commit messages generated by large language models.
59
+
60
+ ---
61
+
62
+ ## What You Get
63
+
64
+ Instead of generic messages like `"update stuff"`, `"fix bug"`, or `"add feature"`, you get intelligent, contextual messages that explain the **why** behind your changes:
65
+
66
+ ![GAC generating a contextual commit message](assets/gac-simple-usage.png)
67
+
68
+ ---
69
+
70
+ ## Quick Start
71
+
72
+ ### Use without installing
73
+
74
+ ```bash
75
+ uvx gac init # Configure your LLM provider
76
+ uvx gac # Generate and commit with LLM
77
+ ```
78
+
79
+ That's it! Review the generated message and confirm with `y`.
80
+
81
+ ### Install gac globally
82
+
83
+ ```bash
84
+ uv tool install gac
85
+ gac init
86
+ gac
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Key Features
92
+
93
+ ### 🌐 **Supported Providers**
94
+
95
+ - **Anthropic** • **Cerebras** • **Chutes.ai** • **Fireworks** • **Gemini**
96
+ - **Groq** • **LM Studio** • **Ollama** • **OpenAI** • **OpenRouter**
97
+ - **Streamlake** • **Synthetic.new** • **Z.AI** • **Z.AI Coding**
98
+
99
+ ### 🧠 **Smart LLM Analysis**
100
+
101
+ - **Understands intent**: Analyzes code structure, logic, and patterns to understand the "why" behind your changes, not just what changed
102
+ - **Semantic awareness**: Recognizes refactoring, bug fixes, features, and breaking changes to generate contextually appropriate messages
103
+ - **Intelligent filtering**: Prioritizes meaningful changes while ignoring generated files, dependencies, and artifacts
104
+
105
+ ### 📝 **Multiple Message Formats**
106
+
107
+ - **One-liner** (-o flag): Single-line commit message following conventional commit format
108
+ - **Standard** (default): Summary with bullet points explaining implementation details
109
+ - **Verbose** (-v flag): Comprehensive explanations including motivation, technical approach, and impact analysis
110
+
111
+ ### 💻 **Developer Experience**
112
+
113
+ - **Interactive feedback**: Regenerate messages with specific requests like `r "make it shorter"` or `r "focus on the bug fix"`
114
+ - **One-command workflows**: Complete workflows with flags like `gac -ayp` (stage all, auto-confirm, push)
115
+ - **Git integration**: Respects pre-commit and lefthook hooks, running them before expensive LLM operations
116
+
117
+ ### 🛡️ **Built-in Security**
118
+
119
+ - **Automatic secret detection**: Scans for API keys, passwords, and tokens before committing
120
+ - **Interactive protection**: Prompts before committing potentially sensitive data with clear remediation options
121
+ - **Smart filtering**: Ignores example files, template files, and placeholder text to reduce false positives
122
+
123
+ ---
124
+
125
+ ## Usage Examples
126
+
127
+ ### Basic Workflow
128
+
129
+ ```bash
130
+ # Stage your changes
131
+ git add .
132
+
133
+ # Generate and commit with LLM
134
+ gac
135
+
136
+ # Review → y (commit) | n (cancel) | r (reroll)
137
+ ```
138
+
139
+ ### Common Commands
140
+
141
+ | Command | Description |
142
+ | --------------- | ---------------------------------------------- |
143
+ | `gac` | Generate commit message |
144
+ | `gac -y` | Auto-confirm (no review needed) |
145
+ | `gac -a` | Stage all + commit |
146
+ | `gac -o` | One-line message only |
147
+ | `gac -v` | Detailed verbose format |
148
+ | `gac -h "hint"` | Add context for LLM (e.g., `gac -h "bug fix"`) |
149
+ | `gac -s` | Include scope (e.g., feat(auth):) |
150
+ | `gac -p` | Commit and push |
151
+
152
+ ### Power User Examples
153
+
154
+ ```bash
155
+ # Complete workflow in one command
156
+ gac -ayp -h "release preparation"
157
+
158
+ # Detailed explanation with scope
159
+ gac -v -s
160
+
161
+ # Quick one-liner for small changes
162
+ gac -o
163
+
164
+ # Debug what the LLM sees
165
+ gac --show-prompt
166
+
167
+ # Skip security scan (use carefully)
168
+ gac --skip-secret-scan
169
+ ```
170
+
171
+ ### Interactive Reroll System
172
+
173
+ Not happy with the result? Use the reroll feature for intelligent regeneration:
174
+
175
+ ```bash
176
+ # Simple regeneration (uses previous context)
177
+ r
178
+
179
+ # With specific feedback
180
+ r make it shorter and focus on the performance improvement
181
+ r use conventional commit format with scope
182
+ r explain the security implications
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Configuration
188
+
189
+ Run `gac init` to configure your provider interactively, or set environment variables:
190
+
191
+ ```bash
192
+ # Example configuration
193
+ GAC_MODEL=anthropic:your-model-name
194
+ OPENAI_API_KEY=your_key_here
195
+ ANTHROPIC_API_KEY=your_key_here
196
+ ```
197
+
198
+ See `.gac.env.example` for all available options.
199
+
200
+ ---
201
+
202
+ ## Getting Help
203
+
204
+ - **Full documentation**: [USAGE.md](USAGE.md) - Complete CLI reference
205
+ - **Troubleshooting**: [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) - Common issues and solutions
206
+ - **Contributing**: [CONTRIBUTING.md](docs/CONTRIBUTING.md) - Development setup and guidelines
207
+
208
+ ---
209
+
210
+ <!-- markdownlint-disable MD033 MD036 -->
211
+
212
+ <div align="center">
213
+
214
+ Made with ❤️ for developers who want better commit messages
215
+
216
+ [⭐ Star us on GitHub](https://github.com/cellwebb/gac) • [🐛 Report issues](https://github.com/cellwebb/gac/issues) • [📖 Full docs](USAGE.md)
217
+
218
+ </div>
219
+
220
+ <!-- markdownlint-enable MD033 MD036 -->
@@ -1,7 +1,7 @@
1
1
  gac/__init__.py,sha256=z9yGInqtycFIT3g1ca24r-A3699hKVaRqGUI79wsmMc,415
2
- gac/__version__.py,sha256=Ac_o0qc9fLvv3b49cGWCas1Mel80pE0GK2LoIIKSgKQ,66
3
- gac/ai.py,sha256=-0rIZQCHC7yOEmkLtCLDzeInnrm960hVpiEELi8NM_U,3513
4
- gac/ai_utils.py,sha256=eqrpiBTueCgSOxUC2b4Ei0G4DM1GMt866sJEIhWpytU,7333
2
+ gac/__version__.py,sha256=aS2cFuCUqXoCZQnjW0wEIKh2TEv5x4FSMZ7E_W-xkiA,67
3
+ gac/ai.py,sha256=QSGPrtFuzZh4LIjIt2HKVtdZ07xtSQXJRT7fxJ-Fufs,3898
4
+ gac/ai_utils.py,sha256=NBxmYAPLj3YcFHuo3TCJ4kpCtpAxnGJ7aG6_1Af9VOw,7314
5
5
  gac/cli.py,sha256=crUUI6osYtE3QAZ7r6DRlVk9gR3X2PctzS1sssVQ9_g,5070
6
6
  gac/config.py,sha256=n3TkQYBqSKkH68QUM6M7kwSK83ghmItoh0p5ZDFnhHA,1746
7
7
  gac/config_cli.py,sha256=v9nFHZO1RvK9fzHyuUS6SG-BCLHMsdOMDwWamBhVVh4,1608
@@ -9,16 +9,17 @@ gac/constants.py,sha256=8GHB7yeK2CYT0t80-k9N6LvgZPe-StNH3dK3NsUO46c,4977
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=wq2MMi1xQrbVTib-5BsVdbPXQkPStB3G3Q8VnSKiKFQ,4740
13
- gac/main.py,sha256=pQUvrJt3cqxKV7b7w7U_Gb9QOQMnoSB7tI0s_3uZAI0,16163
12
+ gac/init_cli.py,sha256=YZAFRWcQ1TeXCGwEEyBeCwai2QYa1U0ErBCE8shkSkU,4804
13
+ gac/main.py,sha256=dJrBSN5rJlbWspLGDx3eUJU4uZFVhvuv7qtgIvF7RH4,14723
14
14
  gac/preprocess.py,sha256=aMxsjGxy9YP752NWjgf0KP5Sn6p8keIJAGlMYr8jDgQ,15373
15
15
  gac/prompt.py,sha256=d_kBXmhf3bDVLyDj8J7AS7GBAxF2jlc8lXoHX3Dzi5k,24255
16
16
  gac/security.py,sha256=15Yp6YR8QC4eECJi1BUCkMteh_veZXUbLL6W8qGcDm4,9920
17
17
  gac/utils.py,sha256=nV42-brIHW_fBg7x855GM8nRrqEBbRzTSweg-GTyGE8,3971
18
- gac/providers/__init__.py,sha256=ejIM5vvmfTp7vfJSNeQQPIEJusOkKTUZpUE7OeWBc9Y,876
18
+ gac/providers/__init__.py,sha256=26G8YJgZh2gK21bNjuDmPOTPeU8kbJz6HmUcvm0TOfw,944
19
19
  gac/providers/anthropic.py,sha256=VK5d1s1PmBNDwh_x7illQ2CIZIHNIYU28btVfizwQPs,2036
20
20
  gac/providers/cerebras.py,sha256=Ik8lhlsliGJVkgDgqlThfpra9tqbdYQZkaC4eNxRd9w,1648
21
21
  gac/providers/chutes.py,sha256=cclJOLuGVIiiaF-9Bs1kH6SSOhEmduGB2zZ86KIaXKw,2617
22
+ gac/providers/fireworks.py,sha256=zsWhf6LMVdtsD9keXRFwgn9lCQigz6VmrDl6vqIVkdI,1688
22
23
  gac/providers/gemini.py,sha256=GZQz6Y9fd5-xk-U4pXn9bXLeBowxDXOYDyWyrtjFurM,2909
23
24
  gac/providers/groq.py,sha256=9v2fAjDa_iRNHFptiUBN8Vt7ZDKkW_JOmIBeYvycD1M,2806
24
25
  gac/providers/lmstudio.py,sha256=R82-f0tWdFfGQxLT6o3Q2tfvYguF7ESUg9DEUHNyrDk,2146
@@ -28,8 +29,8 @@ gac/providers/openrouter.py,sha256=H3ce8JcRUYq1I30lOjGESdX7jfoPkW3mKAYnc2aYfBw,2
28
29
  gac/providers/streamlake.py,sha256=KAA2ZnpuEI5imzvdWVWUhEBHSP0BMnprKXte6CbwBWY,2047
29
30
  gac/providers/synthetic.py,sha256=sRMIJTS9LpcXd9A7qp_ZjZxdqtTKRn9fl1W4YwJZP4c,1855
30
31
  gac/providers/zai.py,sha256=kywhhrCfPBu0rElZyb-iENxQxxpVGykvePuL4xrXlaU,2739
31
- gac-1.9.4.dist-info/METADATA,sha256=5Gz4bjPOlW4y__RWSqdHZzsbGsZ0mva8Ot6_NdofXys,9409
32
- gac-1.9.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
- gac-1.9.4.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
34
- gac-1.9.4.dist-info/licenses/LICENSE,sha256=vOab37NouL1PNs5BswnPayrMCqaN2sqLfMQfqPDrpZg,1103
35
- gac-1.9.4.dist-info/RECORD,,
32
+ gac-1.10.0.dist-info/METADATA,sha256=AxOVZrY2wmdN1Oez8aYF959Rf49NFQYKHpGe16X_pvU,7698
33
+ gac-1.10.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
34
+ gac-1.10.0.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
35
+ gac-1.10.0.dist-info/licenses/LICENSE,sha256=vOab37NouL1PNs5BswnPayrMCqaN2sqLfMQfqPDrpZg,1103
36
+ gac-1.10.0.dist-info/RECORD,,
@@ -1,246 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: gac
3
- Version: 1.9.4
4
- Summary: AI-powered Git commit message generator with multi-provider support
5
- Project-URL: Homepage, https://github.com/cellwebb/gac
6
- Project-URL: Documentation, https://github.com/cellwebb/gac#readme
7
- Project-URL: Repository, https://github.com/cellwebb/gac.git
8
- Project-URL: Issues, https://github.com/cellwebb/gac/issues
9
- Project-URL: Changelog, https://github.com/cellwebb/gac/blob/main/CHANGELOG.md
10
- Project-URL: Source, https://github.com/cellwebb/gac
11
- Author-email: cellwebb <cellwebb@users.noreply.github.com>
12
- License-Expression: MIT
13
- License-File: LICENSE
14
- Classifier: Development Status :: 4 - Beta
15
- Classifier: Programming Language :: Python
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Programming Language :: Python :: 3.14
21
- Classifier: Programming Language :: Python :: Implementation :: CPython
22
- Classifier: Programming Language :: Python :: Implementation :: PyPy
23
- Requires-Python: >=3.10
24
- Requires-Dist: anthropic>=0.68.0
25
- Requires-Dist: click>=8.3.0
26
- Requires-Dist: halo
27
- Requires-Dist: httpcore>=1.0.9
28
- Requires-Dist: httpx>=0.28.0
29
- Requires-Dist: pydantic>=2.12.0
30
- Requires-Dist: python-dotenv>=1.1.1
31
- Requires-Dist: questionary
32
- Requires-Dist: rich>=14.1.0
33
- Requires-Dist: sumy
34
- Requires-Dist: tiktoken>=0.12.0
35
- Provides-Extra: dev
36
- Requires-Dist: build; extra == 'dev'
37
- Requires-Dist: codecov; extra == 'dev'
38
- Requires-Dist: pre-commit; extra == 'dev'
39
- Requires-Dist: pytest; extra == 'dev'
40
- Requires-Dist: pytest-cov; extra == 'dev'
41
- Requires-Dist: ruff; extra == 'dev'
42
- Requires-Dist: twine; extra == 'dev'
43
- Description-Content-Type: text/markdown
44
-
45
- <!-- markdownlint-disable MD013 -->
46
-
47
- # Git Auto Commit (gac)
48
-
49
- [![PyPI version](https://img.shields.io/pypi/v/gac.svg)](https://pypi.org/project/gac/)
50
- [![Python](https://img.shields.io/badge/python-3.10%20|%203.11%20|%203.12%20|%203.13%20|%203.14-blue.svg)](https://www.python.org/downloads/)
51
- [![Build Status](https://github.com/cellwebb/gac/actions/workflows/ci.yml/badge.svg)](https://github.com/cellwebb/gac/actions)
52
- [![codecov](https://codecov.io/gh/cellwebb/gac/branch/main/graph/badge.svg)](https://app.codecov.io/gh/cellwebb/gac)
53
- [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
54
- [![mypy](https://img.shields.io/badge/mypy-checked-blue.svg)](https://mypy-lang.org/)
55
- [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](docs/CONTRIBUTING.md)
56
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
57
-
58
- ## Features
59
-
60
- - **LLM-Powered Commit Messages:** Automatically generates clear, concise, and context-aware commit messages using large language models.
61
- - **Deep Contextual Analysis:** Understands your code by analyzing staged changes, repository structure, and recent commit history to provide highly relevant suggestions.
62
- - **Multi-Provider & Model Support:** Flexibly works with leading AI providers (Anthropic, Cerebras, Chutes.ai, Gemini, Groq, OpenAI, OpenRouter, Streamlake/Vanchin, Synthetic.new, & Z.AI) and local providers (LM Studio & Ollama), easily configured through an interactive setup or environment variables.
63
- - **Seamless Git Workflow:** Integrates smoothly into your existing Git routine as a simple drop-in replacement for `git commit`.
64
- - **Extensive Customization:** Tailor commit messages to your needs with a rich set of flags, including one-liners (`-o`), detailed verbose messages (`-v`), AI hints (`-h`), scope inference (`-s`), and specific model selection (`-m`).
65
- - **Streamlined Workflow Commands:** Boost your productivity with convenient options to stage all changes (`-a`), auto-confirm commits (`-y`), and push to your remote repository (`-p`) in a single step.
66
- - **Interactive Reroll with Feedback:** Not satisfied with the generated commit message? Use `r` for a simple regeneration, or `r <feedback>` to provide specific improvement suggestions (e.g., `r make it shorter`, `r focus on the bug fix`).
67
- - **Token Usage Tracking:** Display token consumption statistics (prompt, completion, and total tokens).
68
- - **Security Scanner:** Built-in detection of secrets and API keys in staged changes to prevent accidental commits of sensitive information.
69
-
70
- ## How It Works
71
-
72
- gac analyzes your staged changes to generate high-quality commit messages with the help of large language models. The tool uses a sophisticated prompt architecture that separates system instructions from user data, enabling better AI understanding and more consistent results.
73
-
74
- ## How to Use
75
-
76
- ```sh
77
- git add .
78
- gac
79
- ```
80
-
81
- ![Simple gac Usage](assets/gac-simple-usage.png)
82
-
83
- ## Installation and Configuration
84
-
85
- ### 1. Installation
86
-
87
- #### Quick Try with uvx (no installation)
88
-
89
- You can try gac without installing it using uvx:
90
-
91
- ```sh
92
- # Try gac without installation
93
- uvx gac --help
94
-
95
- # Set up configuration (creates ~/.gac.env)
96
- uvx gac init
97
-
98
- # Use gac on staged changes
99
- git add .
100
- uvx gac
101
- ```
102
-
103
- #### Permanent Installation
104
-
105
- Install system-wide using pipx from the GitHub repository:
106
-
107
- ```sh
108
- # Install pipx if you don't have it
109
- python3 -m pip install --user pipx
110
- python3 -m pipx ensurepath
111
-
112
- # Install gac
113
- pipx install gac
114
- ```
115
-
116
- Verify installation:
117
-
118
- ```sh
119
- gac --version
120
- ```
121
-
122
- ### 2. Configuration
123
-
124
- The recommended way to configure `gac` is using the interactive setup:
125
-
126
- ```sh
127
- gac init
128
- ```
129
-
130
- This command will guide you through selecting an AI provider, model, and securely entering your API keys. It will create or update a user-level configuration file at `$HOME/.gac.env`.
131
-
132
- Example `$HOME/.gac.env` output:
133
-
134
- ```env
135
- GAC_MODEL=anthropic:claude-3-5-haiku-latest
136
- ANTHROPIC_API_KEY=your_anthropic_key_here
137
- ```
138
-
139
- Alternatively, you can configure `gac` using environment variables or by manually creating/editing the configuration file.
140
-
141
- #### Managing Configuration with `gac config`
142
-
143
- You can manage settings in your `$HOME/.gac.env` file using `gac config` commands:
144
-
145
- - Streamlake uses inference endpoint IDs instead of model names. When prompted, paste the exact endpoint ID from the Streamlake console.
146
- - For local providers like Ollama and LM Studio, gac will ask for the base API URL. API keys are optional for these providers unless your instance requires authentication.
147
-
148
- - Show config: `gac config show`
149
- - Set a value: `gac config set GAC_MODEL groq:meta-llama/llama-4-scout-17b-16e-instruct`
150
- - Get a value: `gac config get GAC_MODEL`
151
- - Unset a value: `gac config unset GAC_MODEL`
152
-
153
- ### 3. Verify Setup
154
-
155
- Test that `gac` is working properly with your configuration:
156
-
157
- ```sh
158
- # Make a change to a file
159
- echo "# Test change" >> README.md
160
- git add README.md
161
- gac -o # Generate a one-line commit message
162
- ```
163
-
164
- You should see an AI-generated commit message.
165
-
166
- ### 4. Upgrade
167
-
168
- To upgrade `gac` to the latest version, run:
169
-
170
- ```sh
171
- pipx upgrade gac
172
- ```
173
-
174
- ## Basic Usage
175
-
176
- Once installed and configured, using `gac` is straightforward:
177
-
178
- 1. Stage your changes:
179
-
180
- ```sh
181
- git add .
182
- ```
183
-
184
- 2. Run `gac`:
185
-
186
- ```sh
187
- gac
188
- ```
189
-
190
- This will generate a commit message for review. Confirm with `y` to accept the message.
191
-
192
- ### Common Commands
193
-
194
- - Generate a commit message: `gac`
195
- - Auto-accept the commit message: `gac -y`
196
- - Stage all changes and generate a commit message: `gac -a`
197
- - Generate a one-line commit message: `gac -o`
198
- - Add a hint for the AI: `gac -h "Fixed the authentication bug"`
199
- - Push the commit (requires accepting the commit message): `gac -p`
200
- - Advanced usage: Add all, auto-confirm, push a one-liner with a hint: `gac -aypo -h "update for release"`
201
- - Skip security scan: `gac --skip-secret-scan` (use with caution)
202
-
203
- ### Security Features
204
-
205
- GAC includes a built-in security scanner to prevent accidental commits of sensitive information:
206
-
207
- - **Automatic Scanning**: By default, scans all staged changes for potential secrets and API keys
208
- - **Interactive Protection**: When secrets are detected, you can:
209
- - Abort the commit (recommended)
210
- - Continue anyway (not recommended)
211
- - Remove affected files and continue
212
- - **Wide Coverage**: Detects AWS keys, GitHub tokens, OpenAI API keys, database URLs, private keys, and more
213
- - **Smart Filtering**: Ignores example keys, placeholders, and test values to reduce false positives
214
-
215
- To disable the security scan (not recommended unless you know what you're doing):
216
-
217
- ```sh
218
- # Skip scan for one command
219
- gac --skip-secret-scan
220
-
221
- # Or disable via environment variable
222
- export GAC_SKIP_SECRET_SCAN=true
223
- gac
224
- ```
225
-
226
- For a full list of CLI flags, advanced options, and example workflows, see [USAGE.md](USAGE.md).
227
-
228
- ## Best Practices
229
-
230
- - gac loads configuration from two locations (in order of precedence):
231
- 1. User-level `$HOME/.gac.env` (applies to all projects for the user)
232
- 2. Project-level `.env` (in the project root, overrides user config if present) Environment variables always take final precedence over both files.
233
- - Keep API keys out of version control
234
- - For troubleshooting, see [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)
235
-
236
- ## Contributing
237
-
238
- We welcome contributions! Please see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for guidelines.
239
-
240
- ## License
241
-
242
- This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.
243
-
244
- ## Community & Support
245
-
246
- For questions, suggestions, or support, please open an issue or discussion on GitHub.
File without changes