gac 0.18.1__py3-none-any.whl → 0.19.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 +1 -1
- gac/cli.py +11 -11
- gac/main.py +3 -3
- gac/prompt.py +21 -75
- {gac-0.18.1.dist-info → gac-0.19.0.dist-info}/METADATA +3 -3
- {gac-0.18.1.dist-info → gac-0.19.0.dist-info}/RECORD +9 -9
- {gac-0.18.1.dist-info → gac-0.19.0.dist-info}/WHEEL +0 -0
- {gac-0.18.1.dist-info → gac-0.19.0.dist-info}/entry_points.txt +0 -0
- {gac-0.18.1.dist-info → gac-0.19.0.dist-info}/licenses/LICENSE +0 -0
gac/__version__.py
CHANGED
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=
|
|
40
|
-
|
|
41
|
-
|
|
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:
|
|
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
|
-
#
|
|
92
|
-
|
|
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
|
-
|
|
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":
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
220
|
+
# Select the appropriate conventions section based on infer_scope parameter
|
|
257
221
|
try:
|
|
258
|
-
logger.debug(f"Processing
|
|
259
|
-
if
|
|
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
|
|
320
|
-
if
|
|
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.
|
|
3
|
+
Version: 0.19.0
|
|
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
|
|
@@ -58,11 +58,11 @@ Description-Content-Type: text/markdown
|
|
|
58
58
|
|
|
59
59
|
## Features
|
|
60
60
|
|
|
61
|
-
- **
|
|
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`),
|
|
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=
|
|
2
|
+
gac/__version__.py,sha256=EzEhcsqRK8CRB_s66jICyS0xRG0K29SUvbzZAdpl0mU,67
|
|
3
3
|
gac/ai.py,sha256=vncnizPce4QbbpddjVORdK2X95rIJwWbJxmIhjscQSU,6357
|
|
4
|
-
gac/cli.py,sha256=
|
|
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=
|
|
12
|
+
gac/main.py,sha256=WI7mxIbL05neQr1VfoopOeZKIonwpwFeZCt_4VFewPY,11987
|
|
13
13
|
gac/preprocess.py,sha256=4igtZ9OTHgTpqwlJmbcGaqzmdD0HHCZJwsZ9eG118Gk,15360
|
|
14
|
-
gac/prompt.py,sha256=
|
|
14
|
+
gac/prompt.py,sha256=_fv24XU3DZE_S72vcdUYnNkmy-_KXnr1Vlc-9okop7E,17263
|
|
15
15
|
gac/utils.py,sha256=W3ladtmsH01MNLdckQYTzYrYbTGEdzCKI36he9C-y_E,3945
|
|
16
|
-
gac-0.
|
|
17
|
-
gac-0.
|
|
18
|
-
gac-0.
|
|
19
|
-
gac-0.
|
|
20
|
-
gac-0.
|
|
16
|
+
gac-0.19.0.dist-info/METADATA,sha256=AU9iYNzYzRDuzMMh_VpbE8B-K2OBHyuEiLyj93Pbl-A,8410
|
|
17
|
+
gac-0.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
+
gac-0.19.0.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
|
|
19
|
+
gac-0.19.0.dist-info/licenses/LICENSE,sha256=s11puNmYfzwoSwG96nhOJe268Y1QFckr8-Hmzo3_eJE,1087
|
|
20
|
+
gac-0.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|