gac 0.18.1__py3-none-any.whl → 0.19.1__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__ = "0.18.1"
3
+ __version__ = "0.19.1"
gac/cli.py CHANGED
@@ -36,10 +36,9 @@ logger = logging.getLogger(__name__)
36
36
  @click.option(
37
37
  "--scope",
38
38
  "-s",
39
- is_flag=False,
40
- flag_value="",
41
- default=None,
42
- help="Add a scope to the commit message. If used without a value, the LLM will determine an appropriate scope.",
39
+ is_flag=True,
40
+ default=False,
41
+ help="Infer an appropriate scope for the commit message",
43
42
  )
44
43
  @click.option("--hint", "-h", default="", help="Additional context to include in the prompt")
45
44
  # Model options
@@ -65,7 +64,7 @@ def cli(
65
64
  one_liner: bool = False,
66
65
  push: bool = False,
67
66
  show_prompt: bool = False,
68
- scope: str = None,
67
+ scope: bool = False,
69
68
  quiet: bool = False,
70
69
  yes: bool = False,
71
70
  hint: str = "",
@@ -88,10 +87,8 @@ def cli(
88
87
  setup_logging(effective_log_level)
89
88
  logger.info("Starting gac")
90
89
 
91
- # Apply always_include_scope setting if no explicit scope provided
92
- effective_scope = scope
93
- if scope is None and config.get("always_include_scope", False):
94
- effective_scope = "" # Empty string triggers scope inference
90
+ # Determine if we should infer scope based on -s flag or always_include_scope setting
91
+ infer_scope = bool(scope or config.get("always_include_scope", False))
95
92
 
96
93
  try:
97
94
  main(
@@ -100,7 +97,7 @@ def cli(
100
97
  hint=hint,
101
98
  one_liner=one_liner,
102
99
  show_prompt=show_prompt,
103
- scope=effective_scope,
100
+ infer_scope=bool(infer_scope),
104
101
  require_confirmation=not yes,
105
102
  push=push,
106
103
  quiet=quiet,
@@ -110,13 +107,16 @@ def cli(
110
107
  except Exception as e:
111
108
  handle_error(e, exit_program=True)
112
109
  else:
110
+ # Determine if we should infer scope based on -s flag or always_include_scope setting
111
+ infer_scope = bool(scope or config.get("always_include_scope", False))
112
+
113
113
  ctx.obj = {
114
114
  "add_all": add_all,
115
115
  "log_level": log_level,
116
116
  "one_liner": one_liner,
117
117
  "push": push,
118
118
  "show_prompt": show_prompt,
119
- "scope": scope,
119
+ "scope": infer_scope,
120
120
  "quiet": quiet,
121
121
  "yes": yes,
122
122
  "hint": hint,
gac/main.py CHANGED
@@ -34,7 +34,7 @@ def main(
34
34
  hint: str = "",
35
35
  one_liner: bool = False,
36
36
  show_prompt: bool = False,
37
- scope: str | None = None,
37
+ infer_scope: bool = False,
38
38
  require_confirmation: bool = True,
39
39
  push: bool = False,
40
40
  quiet: bool = False,
@@ -101,7 +101,7 @@ def main(
101
101
  diff_stat=diff_stat,
102
102
  one_liner=one_liner,
103
103
  hint=hint,
104
- scope=scope,
104
+ infer_scope=infer_scope,
105
105
  )
106
106
 
107
107
  if show_prompt:
@@ -202,7 +202,7 @@ def main(
202
202
  diff_stat=diff_stat,
203
203
  one_liner=one_liner,
204
204
  hint=combined_hint,
205
- scope=scope,
205
+ infer_scope=infer_scope,
206
206
  )
207
207
  else:
208
208
  # No hint given, just reroll with same prompts
gac/prompt.py CHANGED
@@ -73,43 +73,7 @@ If you cannot confidently determine a type, use 'chore'.
73
73
  Do NOT include a scope in your commit prefix.
74
74
  </conventions_no_scope>
75
75
 
76
- <conventions_scope_provided>
77
- You MUST write a conventional commit message with EXACTLY ONE type and the REQUIRED scope '{scope}'.
78
-
79
- FORMAT: type({scope}): description
80
-
81
- IMPORTANT: Check file types FIRST when determining the commit type:
82
- - If changes are ONLY to documentation files (*.md, *.rst, *.txt in docs/, README*, CHANGELOG*, etc.), ALWAYS use 'docs'
83
- - If changes include both documentation and code, use the prefix for the code changes, unless it is a documentation-only change
84
-
85
- Select ONE type from this list that best matches the primary purpose of the changes:
86
- - feat: A new feature or functionality addition
87
- - fix: A bug fix or error correction
88
- - docs: Documentation changes only (INCLUDING README and CHANGELOG updates, regardless of how significant)
89
- - style: Changes to code style/formatting without logic changes
90
- - refactor: Code restructuring without behavior changes
91
- - perf: Performance improvements
92
- - test: Adding/modifying tests
93
- - build: Changes to build system/dependencies
94
- - ci: Changes to CI configuration
95
- - chore: Miscellaneous changes not affecting src/test files
96
-
97
- CORRECT EXAMPLES (these formats are correct):
98
- ✅ feat({scope}): add new feature
99
- ✅ fix({scope}): resolve bug
100
- ✅ refactor({scope}): improve code structure
101
- ✅ chore({scope}): update dependencies
102
-
103
- INCORRECT EXAMPLES (these formats are wrong and must NOT be used):
104
- ❌ chore: feat({scope}): description
105
- ❌ fix: refactor({scope}): description
106
- ❌ feat: feat({scope}): description
107
- ❌ chore: chore({scope}): description
108
-
109
- You MUST NOT prefix the type({scope}) with another type. Use EXACTLY ONE type, which MUST include the scope in parentheses.
110
- </conventions_scope_provided>
111
-
112
- <conventions_scope_inferred>
76
+ <conventions_with_scope>
113
77
  You MUST write a conventional commit message with EXACTLY ONE type and an inferred scope.
114
78
 
115
79
  FORMAT: type(scope): description
@@ -156,7 +120,7 @@ INCORRECT EXAMPLES (these formats are wrong and must NOT be used):
156
120
  ❌ chore: chore(component): description
157
121
 
158
122
  You MUST NOT prefix the type(scope) with another type. Use EXACTLY ONE type, which MUST include the scope in parentheses.
159
- </conventions_scope_inferred>
123
+ </conventions_with_scope>
160
124
 
161
125
  <hint>
162
126
  Additional context provided by the user: <hint_text></hint_text>
@@ -235,8 +199,8 @@ def build_prompt(
235
199
  processed_diff: str,
236
200
  diff_stat: str = "",
237
201
  one_liner: bool = False,
202
+ infer_scope: bool = False,
238
203
  hint: str = "",
239
- scope: str | None = None,
240
204
  ) -> tuple[str, str]:
241
205
  """Build system and user prompts for the AI model using the provided template and git information.
242
206
 
@@ -245,47 +209,29 @@ def build_prompt(
245
209
  processed_diff: Git diff output, already preprocessed and ready to use
246
210
  diff_stat: Git diff stat output showing file changes summary
247
211
  one_liner: Whether to request a one-line commit message
212
+ infer_scope: Whether to infer scope for the commit message
248
213
  hint: Optional hint to guide the AI
249
- scope: Optional scope parameter. None = no scope, "infer" = infer scope, any other string = use as scope
250
214
 
251
215
  Returns:
252
216
  Tuple of (system_prompt, user_prompt) ready to be sent to an AI model
253
217
  """
254
218
  template = load_prompt_template()
255
219
 
256
- # Select the appropriate conventions section based on scope parameter
220
+ # Select the appropriate conventions section based on infer_scope parameter
257
221
  try:
258
- logger.debug(f"Processing scope parameter: {scope}")
259
- if scope is None:
222
+ logger.debug(f"Processing infer_scope parameter: {infer_scope}")
223
+ if infer_scope:
224
+ # User wants to infer a scope from changes (any value other than None)
225
+ logger.debug("Using inferred-scope conventions")
226
+ template = re.sub(r"<conventions_no_scope>.*?</conventions_no_scope>\n", "", template, flags=re.DOTALL)
227
+ template = template.replace("<conventions_with_scope>", "<conventions>")
228
+ template = template.replace("</conventions_with_scope>", "</conventions>")
229
+ else:
260
230
  # No scope - use the plain conventions section
261
231
  logger.debug("Using no-scope conventions")
262
- template = re.sub(
263
- r"<conventions_scope_provided>.*?</conventions_scope_provided>\n", "", template, flags=re.DOTALL
264
- )
265
- template = re.sub(
266
- r"<conventions_scope_inferred>.*?</conventions_scope_inferred>\n", "", template, flags=re.DOTALL
267
- )
232
+ template = re.sub(r"<conventions_with_scope>.*?</conventions_with_scope>\n", "", template, flags=re.DOTALL)
268
233
  template = template.replace("<conventions_no_scope>", "<conventions>")
269
234
  template = template.replace("</conventions_no_scope>", "</conventions>")
270
- elif scope == "infer" or scope == "":
271
- # User wants to infer a scope from changes (either with "infer" or empty string)
272
- logger.debug(f"Using inferred-scope conventions (scope={scope})")
273
- template = re.sub(
274
- r"<conventions_scope_provided>.*?</conventions_scope_provided>\n", "", template, flags=re.DOTALL
275
- )
276
- template = re.sub(r"<conventions_no_scope>.*?</conventions_no_scope>\n", "", template, flags=re.DOTALL)
277
- template = template.replace("<conventions_scope_inferred>", "<conventions>")
278
- template = template.replace("</conventions_scope_inferred>", "</conventions>")
279
- else:
280
- # User provided a specific scope
281
- logger.debug(f"Using provided-scope conventions with scope '{scope}'")
282
- template = re.sub(
283
- r"<conventions_scope_inferred>.*?</conventions_scope_inferred>\n", "", template, flags=re.DOTALL
284
- )
285
- template = re.sub(r"<conventions_no_scope>.*?</conventions_no_scope>\n", "", template, flags=re.DOTALL)
286
- template = template.replace("<conventions_scope_provided>", "<conventions>")
287
- template = template.replace("</conventions_scope_provided>", "</conventions>")
288
- template = template.replace("{scope}", scope)
289
235
  except Exception as e:
290
236
  logger.error(f"Error processing scope parameter: {e}")
291
237
  # Fallback to no scope if there's an error
@@ -316,17 +262,17 @@ def build_prompt(
316
262
  else:
317
263
  template = re.sub(r"<one_liner>.*?</one_liner>", "", template, flags=re.DOTALL)
318
264
 
319
- # Clean up examples sections based on scope settings
320
- if scope is None:
265
+ # Clean up examples sections based on infer_scope settings
266
+ if infer_scope:
267
+ # With scope (inferred) - keep scope examples, remove no_scope examples
268
+ template = re.sub(r"<examples_no_scope>.*?</examples_no_scope>\n?", "", template, flags=re.DOTALL)
269
+ template = template.replace("<examples_with_scope>", "<examples>")
270
+ template = template.replace("</examples_with_scope>", "</examples>")
271
+ else:
321
272
  # No scope - keep no_scope examples, remove scope examples
322
273
  template = re.sub(r"<examples_with_scope>.*?</examples_with_scope>\n?", "", template, flags=re.DOTALL)
323
274
  template = template.replace("<examples_no_scope>", "<examples>")
324
275
  template = template.replace("</examples_no_scope>", "</examples>")
325
- else:
326
- # With scope (either provided or inferred) - keep scope examples, remove no_scope examples
327
- template = re.sub(r"<examples_no_scope>.*?</examples_no_scope>\n?", "", template, flags=re.DOTALL)
328
- template = template.replace("<examples_with_scope>", "<examples>")
329
- template = template.replace("</examples_with_scope>", "</examples>")
330
276
 
331
277
  # Clean up extra whitespace, collapsing blank lines that may contain spaces
332
278
  template = re.sub(r"\n(?:[ \t]*\n){2,}", "\n\n", template)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gac
3
- Version: 0.18.1
3
+ Version: 0.19.1
4
4
  Summary: AI-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
@@ -20,20 +20,20 @@ Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Programming Language :: Python :: Implementation :: CPython
21
21
  Classifier: Programming Language :: Python :: Implementation :: PyPy
22
22
  Requires-Python: >=3.10
23
- Requires-Dist: aisuite
24
- Requires-Dist: anthropic
25
- Requires-Dist: cerebras-cloud-sdk
26
- Requires-Dist: click
23
+ Requires-Dist: aisuite>=0.1.11
24
+ Requires-Dist: anthropic>=0.68.0
25
+ Requires-Dist: cerebras-cloud-sdk==1.49.0
26
+ Requires-Dist: click>=8.3.0
27
27
  Requires-Dist: docstring-parser
28
- Requires-Dist: groq
28
+ Requires-Dist: groq>=0.31.1
29
29
  Requires-Dist: halo
30
- Requires-Dist: ollama
31
- Requires-Dist: openai
32
- Requires-Dist: pydantic
33
- Requires-Dist: python-dotenv
30
+ Requires-Dist: ollama>=0.5.4
31
+ Requires-Dist: openai>=1.108.1
32
+ Requires-Dist: pydantic>=2.11.9
33
+ Requires-Dist: python-dotenv>=1.1.1
34
34
  Requires-Dist: questionary
35
- Requires-Dist: rich
36
- Requires-Dist: tiktoken
35
+ Requires-Dist: rich>=14.1.0
36
+ Requires-Dist: tiktoken>=0.11.0
37
37
  Provides-Extra: dev
38
38
  Requires-Dist: build; extra == 'dev'
39
39
  Requires-Dist: bump-my-version; extra == 'dev'
@@ -58,11 +58,11 @@ Description-Content-Type: text/markdown
58
58
 
59
59
  ## Features
60
60
 
61
- - **AI-Powered Commit Messages:** Automatically generates clear, concise, and context-aware commit messages using large language models.
61
+ - **LLM-Powered Commit Messages:** Automatically generates clear, concise, and context-aware commit messages using large language models.
62
62
  - **Deep Contextual Analysis:** Understands your code by analyzing staged changes, repository structure, and recent commit history to provide highly relevant suggestions.
63
63
  - **Multi-Provider & Model Support:** Flexibly works with various leading AI providers (like Anthropic, Cerebras, Groq, OpenAI) and models, easily configured through an interactive setup or environment variables.
64
64
  - **Seamless Git Workflow:** Integrates smoothly into your existing Git routine as a simple drop-in replacement for `git commit`.
65
- - **Extensive Customization:** Tailor commit messages to your needs with a rich set of flags, including one-liners (`-o`), AI hints (`-h`), commit scope (`-s`), and specific model selection (`-m`).
65
+ - **Extensive Customization:** Tailor commit messages to your needs with a rich set of flags, including one-liners (`-o`), AI hints (`-h`), scope inference (`-s`), and specific model selection (`-m`).
66
66
  - **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.
67
67
  - **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`).
68
68
  - **Token Usage Tracking:** Display token consumption statistics (prompt, completion, and total tokens).
@@ -1,7 +1,7 @@
1
1
  gac/__init__.py,sha256=z9yGInqtycFIT3g1ca24r-A3699hKVaRqGUI79wsmMc,415
2
- gac/__version__.py,sha256=FEdRQL4YfxDEwmcVMleEDpoi0wk6-1Ky9NDE0G82At4,67
2
+ gac/__version__.py,sha256=hyGMebb4FUEdQREZTwJbEWXUpawroToZSIoTm_-QyVs,67
3
3
  gac/ai.py,sha256=vncnizPce4QbbpddjVORdK2X95rIJwWbJxmIhjscQSU,6357
4
- gac/cli.py,sha256=KsagQerqcf2uqGS4HjV_U1AUBtY3DPmYLsTj08riZmE,4545
4
+ gac/cli.py,sha256=eQS8S7v6p0CfN9wtr239ujYGTi9rKl-KV7STX2U-C3w,4581
5
5
  gac/config.py,sha256=wSgEDjtis7Vk1pv5VPvYmJyD9-tymDS6GiUHjnCMbIM,1486
6
6
  gac/config_cli.py,sha256=v9nFHZO1RvK9fzHyuUS6SG-BCLHMsdOMDwWamBhVVh4,1608
7
7
  gac/constants.py,sha256=MAxdASGncfZY1TdKGdhJZ0wvTBEU3gTN6KEdw8n3Bd8,4844
@@ -9,12 +9,12 @@ gac/diff_cli.py,sha256=wnVQ9OFGnM0d2Pj9WVjWbo0jxqIuRHVAwmb8wU9Pa3E,5676
9
9
  gac/errors.py,sha256=3vIRMQ2QF3sP9_rPfXAFuu5ZSjIVX4FxM-FAuiR8N-8,7416
10
10
  gac/git.py,sha256=MS2m4fv8h4mau1djFG1aje9NXTmkGsjPO9w18LqNGX0,6031
11
11
  gac/init_cli.py,sha256=aNllguofrcLn0ML9tzLVWFkPbwlAvCM9m7undHhMLEo,1825
12
- gac/main.py,sha256=IlT6iOsdwb0uSJJ1A69FaBrq-gFGpnhCcAjJLdHp20w,11962
12
+ gac/main.py,sha256=WI7mxIbL05neQr1VfoopOeZKIonwpwFeZCt_4VFewPY,11987
13
13
  gac/preprocess.py,sha256=4igtZ9OTHgTpqwlJmbcGaqzmdD0HHCZJwsZ9eG118Gk,15360
14
- gac/prompt.py,sha256=Wd-CguIQnVNj_49dXz0Da2Osxx8sfTzRypx_rq2rFWk,20052
14
+ gac/prompt.py,sha256=_fv24XU3DZE_S72vcdUYnNkmy-_KXnr1Vlc-9okop7E,17263
15
15
  gac/utils.py,sha256=W3ladtmsH01MNLdckQYTzYrYbTGEdzCKI36he9C-y_E,3945
16
- gac-0.18.1.dist-info/METADATA,sha256=ZnTlQJHNf_5xWBy_o362nncpqfG7u4scdyPFdmLAqbI,8406
17
- gac-0.18.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
- gac-0.18.1.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
19
- gac-0.18.1.dist-info/licenses/LICENSE,sha256=s11puNmYfzwoSwG96nhOJe268Y1QFckr8-Hmzo3_eJE,1087
20
- gac-0.18.1.dist-info/RECORD,,
16
+ gac-0.19.1.dist-info/METADATA,sha256=jpY9N8ZkA0n30pqIf_5yOxXZVHBtbzUm2FkROBYt9YE,8496
17
+ gac-0.19.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ gac-0.19.1.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
19
+ gac-0.19.1.dist-info/licenses/LICENSE,sha256=s11puNmYfzwoSwG96nhOJe268Y1QFckr8-Hmzo3_eJE,1087
20
+ gac-0.19.1.dist-info/RECORD,,
File without changes