gac 1.13.1__tar.gz → 1.15.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.
- {gac-1.13.1 → gac-1.15.0}/PKG-INFO +13 -1
- {gac-1.13.1 → gac-1.15.0}/README.md +12 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/__version__.py +1 -1
- {gac-1.13.1 → gac-1.15.0}/src/gac/cli.py +12 -1
- {gac-1.13.1 → gac-1.15.0}/src/gac/config.py +3 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/constants.py +136 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/init_cli.py +75 -0
- gac-1.15.0/src/gac/language_cli.py +111 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/main.py +18 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/preprocess.py +3 -3
- {gac-1.13.1 → gac-1.15.0}/src/gac/prompt.py +347 -229
- {gac-1.13.1 → gac-1.15.0}/.gitignore +0 -0
- {gac-1.13.1 → gac-1.15.0}/LICENSE +0 -0
- {gac-1.13.1 → gac-1.15.0}/pyproject.toml +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/__init__.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/ai.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/ai_utils.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/config_cli.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/diff_cli.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/errors.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/git.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/__init__.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/anthropic.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/cerebras.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/chutes.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/custom_anthropic.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/custom_openai.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/deepseek.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/fireworks.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/gemini.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/groq.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/lmstudio.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/minimax.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/ollama.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/openai.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/openrouter.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/streamlake.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/synthetic.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/together.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/providers/zai.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/security.py +0 -0
- {gac-1.13.1 → gac-1.15.0}/src/gac/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gac
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.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
|
|
@@ -108,6 +108,13 @@ gac
|
|
|
108
108
|
- **Standard** (default): Summary with bullet points explaining implementation details
|
|
109
109
|
- **Verbose** (-v flag): Comprehensive explanations including motivation, technical approach, and impact analysis
|
|
110
110
|
|
|
111
|
+
### 🌍 **Multilingual Support**
|
|
112
|
+
|
|
113
|
+
- **25+ languages**: Generate commit messages in English, Chinese, Japanese, Korean, Spanish, French, German, and 20+ more languages
|
|
114
|
+
- **Flexible translation**: Choose to keep conventional commit prefixes in English for tool compatibility, or fully translate them
|
|
115
|
+
- **Multiple workflows**: Set a default language with `gac language`, or use `-l <language>` flag for one-time overrides
|
|
116
|
+
- **Native script support**: Full support for non-Latin scripts including CJK, Cyrillic, Arabic, and more
|
|
117
|
+
|
|
111
118
|
### 💻 **Developer Experience**
|
|
112
119
|
|
|
113
120
|
- **Interactive feedback**: Regenerate messages with specific requests like `r "make it shorter"` or `r "focus on the bug fix"`
|
|
@@ -197,11 +204,16 @@ ANTHROPIC_API_KEY=your_key_here
|
|
|
197
204
|
|
|
198
205
|
See `.gac.env.example` for all available options.
|
|
199
206
|
|
|
207
|
+
**Want commit messages in another language?** Run `gac language` to select from 25+ languages including Español, Français, 日本語, and more.
|
|
208
|
+
|
|
209
|
+
**Want to customize commit message style?** See [docs/CUSTOM_SYSTEM_PROMPTS.md](docs/CUSTOM_SYSTEM_PROMPTS.md) for guidance on writing custom system prompts.
|
|
210
|
+
|
|
200
211
|
---
|
|
201
212
|
|
|
202
213
|
## Getting Help
|
|
203
214
|
|
|
204
215
|
- **Full documentation**: [USAGE.md](USAGE.md) - Complete CLI reference
|
|
216
|
+
- **Custom prompts**: [CUSTOM_SYSTEM_PROMPTS.md](docs/CUSTOM_SYSTEM_PROMPTS.md) - Customize commit message style
|
|
205
217
|
- **Troubleshooting**: [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) - Common issues and solutions
|
|
206
218
|
- **Contributing**: [CONTRIBUTING.md](docs/CONTRIBUTING.md) - Development setup and guidelines
|
|
207
219
|
|
|
@@ -67,6 +67,13 @@ gac
|
|
|
67
67
|
- **Standard** (default): Summary with bullet points explaining implementation details
|
|
68
68
|
- **Verbose** (-v flag): Comprehensive explanations including motivation, technical approach, and impact analysis
|
|
69
69
|
|
|
70
|
+
### 🌍 **Multilingual Support**
|
|
71
|
+
|
|
72
|
+
- **25+ languages**: Generate commit messages in English, Chinese, Japanese, Korean, Spanish, French, German, and 20+ more languages
|
|
73
|
+
- **Flexible translation**: Choose to keep conventional commit prefixes in English for tool compatibility, or fully translate them
|
|
74
|
+
- **Multiple workflows**: Set a default language with `gac language`, or use `-l <language>` flag for one-time overrides
|
|
75
|
+
- **Native script support**: Full support for non-Latin scripts including CJK, Cyrillic, Arabic, and more
|
|
76
|
+
|
|
70
77
|
### 💻 **Developer Experience**
|
|
71
78
|
|
|
72
79
|
- **Interactive feedback**: Regenerate messages with specific requests like `r "make it shorter"` or `r "focus on the bug fix"`
|
|
@@ -156,11 +163,16 @@ ANTHROPIC_API_KEY=your_key_here
|
|
|
156
163
|
|
|
157
164
|
See `.gac.env.example` for all available options.
|
|
158
165
|
|
|
166
|
+
**Want commit messages in another language?** Run `gac language` to select from 25+ languages including Español, Français, 日本語, and more.
|
|
167
|
+
|
|
168
|
+
**Want to customize commit message style?** See [docs/CUSTOM_SYSTEM_PROMPTS.md](docs/CUSTOM_SYSTEM_PROMPTS.md) for guidance on writing custom system prompts.
|
|
169
|
+
|
|
159
170
|
---
|
|
160
171
|
|
|
161
172
|
## Getting Help
|
|
162
173
|
|
|
163
174
|
- **Full documentation**: [USAGE.md](USAGE.md) - Complete CLI reference
|
|
175
|
+
- **Custom prompts**: [CUSTOM_SYSTEM_PROMPTS.md](docs/CUSTOM_SYSTEM_PROMPTS.md) - Customize commit message style
|
|
164
176
|
- **Troubleshooting**: [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) - Common issues and solutions
|
|
165
177
|
- **Contributing**: [CONTRIBUTING.md](docs/CONTRIBUTING.md) - Development setup and guidelines
|
|
166
178
|
|
|
@@ -13,10 +13,11 @@ import click
|
|
|
13
13
|
from gac import __version__
|
|
14
14
|
from gac.config import load_config
|
|
15
15
|
from gac.config_cli import config as config_cli
|
|
16
|
-
from gac.constants import Logging
|
|
16
|
+
from gac.constants import Languages, Logging
|
|
17
17
|
from gac.diff_cli import diff as diff_cli
|
|
18
18
|
from gac.errors import handle_error
|
|
19
19
|
from gac.init_cli import init as init_cli
|
|
20
|
+
from gac.language_cli import language as language_cli
|
|
20
21
|
from gac.main import main
|
|
21
22
|
from gac.utils import setup_logging
|
|
22
23
|
|
|
@@ -43,6 +44,9 @@ logger = logging.getLogger(__name__)
|
|
|
43
44
|
@click.option("--hint", "-h", default="", help="Additional context to include in the prompt")
|
|
44
45
|
# Model options
|
|
45
46
|
@click.option("--model", "-m", help="Override the default model (format: 'provider:model_name')")
|
|
47
|
+
@click.option(
|
|
48
|
+
"--language", "-l", help="Override the language for commit messages (e.g., 'Spanish', 'es', 'zh-CN', 'ja')"
|
|
49
|
+
)
|
|
46
50
|
# Output options
|
|
47
51
|
@click.option("--quiet", "-q", is_flag=True, help="Suppress non-error output")
|
|
48
52
|
@click.option(
|
|
@@ -75,6 +79,7 @@ def cli(
|
|
|
75
79
|
yes: bool = False,
|
|
76
80
|
hint: str = "",
|
|
77
81
|
model: str | None = None,
|
|
82
|
+
language: str | None = None,
|
|
78
83
|
version: bool = False,
|
|
79
84
|
dry_run: bool = False,
|
|
80
85
|
verbose: bool = False,
|
|
@@ -98,6 +103,9 @@ def cli(
|
|
|
98
103
|
# Determine if verbose mode should be enabled based on -v flag or verbose config setting
|
|
99
104
|
use_verbose = bool(verbose or config.get("verbose", False))
|
|
100
105
|
|
|
106
|
+
# Resolve language code to full name if provided
|
|
107
|
+
resolved_language = Languages.resolve_code(language) if language else None
|
|
108
|
+
|
|
101
109
|
try:
|
|
102
110
|
main(
|
|
103
111
|
stage_all=add_all,
|
|
@@ -113,6 +121,7 @@ def cli(
|
|
|
113
121
|
verbose=use_verbose,
|
|
114
122
|
no_verify=no_verify,
|
|
115
123
|
skip_secret_scan=skip_secret_scan or bool(config.get("skip_secret_scan", False)),
|
|
124
|
+
language=resolved_language,
|
|
116
125
|
)
|
|
117
126
|
except Exception as e:
|
|
118
127
|
handle_error(e, exit_program=True)
|
|
@@ -131,6 +140,7 @@ def cli(
|
|
|
131
140
|
"yes": yes,
|
|
132
141
|
"hint": hint,
|
|
133
142
|
"model": model,
|
|
143
|
+
"language": language,
|
|
134
144
|
"version": version,
|
|
135
145
|
"dry_run": dry_run,
|
|
136
146
|
"verbose": verbose,
|
|
@@ -141,6 +151,7 @@ def cli(
|
|
|
141
151
|
|
|
142
152
|
cli.add_command(config_cli)
|
|
143
153
|
cli.add_command(init_cli)
|
|
154
|
+
cli.add_command(language_cli)
|
|
144
155
|
cli.add_command(diff_cli)
|
|
145
156
|
|
|
146
157
|
if __name__ == "__main__":
|
|
@@ -38,6 +38,9 @@ def load_config() -> dict[str, str | int | float | bool | None]:
|
|
|
38
38
|
"skip_secret_scan": os.getenv("GAC_SKIP_SECRET_SCAN", str(EnvDefaults.SKIP_SECRET_SCAN)).lower()
|
|
39
39
|
in ("true", "1", "yes", "on"),
|
|
40
40
|
"verbose": os.getenv("GAC_VERBOSE", str(EnvDefaults.VERBOSE)).lower() in ("true", "1", "yes", "on"),
|
|
41
|
+
"system_prompt_path": os.getenv("GAC_SYSTEM_PROMPT_PATH"),
|
|
42
|
+
"language": os.getenv("GAC_LANGUAGE"),
|
|
43
|
+
"translate_prefixes": os.getenv("GAC_TRANSLATE_PREFIXES", "false").lower() in ("true", "1", "yes", "on"),
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
return config
|
|
@@ -150,3 +150,139 @@ class CodePatternImportance:
|
|
|
150
150
|
r"\+\s*(test|describe|it|should)\s*\(": 1.1, # Test definitions
|
|
151
151
|
r"\+\s*(assert|expect)": 1.0, # Assertions
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class Languages:
|
|
156
|
+
"""Language code mappings and utilities."""
|
|
157
|
+
|
|
158
|
+
# Language code to full name mapping
|
|
159
|
+
# Supports ISO 639-1 codes and common variants
|
|
160
|
+
CODE_MAP: dict[str, str] = {
|
|
161
|
+
# English
|
|
162
|
+
"en": "English",
|
|
163
|
+
# Chinese
|
|
164
|
+
"zh": "Simplified Chinese",
|
|
165
|
+
"zh-cn": "Simplified Chinese",
|
|
166
|
+
"zh-hans": "Simplified Chinese",
|
|
167
|
+
"zh-tw": "Traditional Chinese",
|
|
168
|
+
"zh-hant": "Traditional Chinese",
|
|
169
|
+
# Japanese
|
|
170
|
+
"ja": "Japanese",
|
|
171
|
+
# Korean
|
|
172
|
+
"ko": "Korean",
|
|
173
|
+
# Spanish
|
|
174
|
+
"es": "Spanish",
|
|
175
|
+
# Portuguese
|
|
176
|
+
"pt": "Portuguese",
|
|
177
|
+
# French
|
|
178
|
+
"fr": "French",
|
|
179
|
+
# German
|
|
180
|
+
"de": "German",
|
|
181
|
+
# Russian
|
|
182
|
+
"ru": "Russian",
|
|
183
|
+
# Hindi
|
|
184
|
+
"hi": "Hindi",
|
|
185
|
+
# Italian
|
|
186
|
+
"it": "Italian",
|
|
187
|
+
# Polish
|
|
188
|
+
"pl": "Polish",
|
|
189
|
+
# Turkish
|
|
190
|
+
"tr": "Turkish",
|
|
191
|
+
# Dutch
|
|
192
|
+
"nl": "Dutch",
|
|
193
|
+
# Vietnamese
|
|
194
|
+
"vi": "Vietnamese",
|
|
195
|
+
# Thai
|
|
196
|
+
"th": "Thai",
|
|
197
|
+
# Indonesian
|
|
198
|
+
"id": "Indonesian",
|
|
199
|
+
# Swedish
|
|
200
|
+
"sv": "Swedish",
|
|
201
|
+
# Arabic
|
|
202
|
+
"ar": "Arabic",
|
|
203
|
+
# Hebrew
|
|
204
|
+
"he": "Hebrew",
|
|
205
|
+
# Greek
|
|
206
|
+
"el": "Greek",
|
|
207
|
+
# Danish
|
|
208
|
+
"da": "Danish",
|
|
209
|
+
# Norwegian
|
|
210
|
+
"no": "Norwegian",
|
|
211
|
+
"nb": "Norwegian",
|
|
212
|
+
"nn": "Norwegian",
|
|
213
|
+
# Finnish
|
|
214
|
+
"fi": "Finnish",
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def resolve_code(language: str) -> str:
|
|
219
|
+
"""Resolve a language code to its full name.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
language: Language name or code (e.g., 'Spanish', 'es', 'zh-CN')
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Full language name (e.g., 'Spanish', 'Simplified Chinese')
|
|
226
|
+
|
|
227
|
+
If the input is already a full language name, it's returned as-is.
|
|
228
|
+
If it's a recognized code, it's converted to the full name.
|
|
229
|
+
Otherwise, the input is returned unchanged (for custom languages).
|
|
230
|
+
"""
|
|
231
|
+
# Normalize the code to lowercase for lookup
|
|
232
|
+
code_lower = language.lower().strip()
|
|
233
|
+
|
|
234
|
+
# Check if it's a recognized code
|
|
235
|
+
if code_lower in Languages.CODE_MAP:
|
|
236
|
+
return Languages.CODE_MAP[code_lower]
|
|
237
|
+
|
|
238
|
+
# Return as-is (could be a full name or custom language)
|
|
239
|
+
return language
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class CommitMessageConstants:
|
|
243
|
+
"""Constants for commit message generation and cleaning."""
|
|
244
|
+
|
|
245
|
+
# Conventional commit type prefixes
|
|
246
|
+
CONVENTIONAL_PREFIXES: list[str] = [
|
|
247
|
+
"feat",
|
|
248
|
+
"fix",
|
|
249
|
+
"docs",
|
|
250
|
+
"style",
|
|
251
|
+
"refactor",
|
|
252
|
+
"perf",
|
|
253
|
+
"test",
|
|
254
|
+
"build",
|
|
255
|
+
"ci",
|
|
256
|
+
"chore",
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
# XML tags that may leak from prompt templates into AI responses
|
|
260
|
+
XML_TAGS_TO_REMOVE: list[str] = [
|
|
261
|
+
"<git-status>",
|
|
262
|
+
"</git-status>",
|
|
263
|
+
"<git_status>",
|
|
264
|
+
"</git_status>",
|
|
265
|
+
"<git-diff>",
|
|
266
|
+
"</git-diff>",
|
|
267
|
+
"<git_diff>",
|
|
268
|
+
"</git_diff>",
|
|
269
|
+
"<repository_context>",
|
|
270
|
+
"</repository_context>",
|
|
271
|
+
"<instructions>",
|
|
272
|
+
"</instructions>",
|
|
273
|
+
"<format>",
|
|
274
|
+
"</format>",
|
|
275
|
+
"<conventions>",
|
|
276
|
+
"</conventions>",
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
# Indicators that mark the start of the actual commit message in AI responses
|
|
280
|
+
COMMIT_INDICATORS: list[str] = [
|
|
281
|
+
"# Your commit message:",
|
|
282
|
+
"Your commit message:",
|
|
283
|
+
"The commit message is:",
|
|
284
|
+
"Here's the commit message:",
|
|
285
|
+
"Commit message:",
|
|
286
|
+
"Final commit message:",
|
|
287
|
+
"# Commit Message",
|
|
288
|
+
]
|
|
@@ -151,4 +151,79 @@ def init() -> None:
|
|
|
151
151
|
elif is_ollama or is_lmstudio:
|
|
152
152
|
click.echo("Skipping API key. You can add one later if needed.")
|
|
153
153
|
|
|
154
|
+
# Language selection
|
|
155
|
+
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]
|
|
186
|
+
language_selection = questionary.select(
|
|
187
|
+
"Select a language for commit messages:", choices=display_names, use_shortcuts=True, use_arrow_keys=True
|
|
188
|
+
).ask()
|
|
189
|
+
|
|
190
|
+
if not language_selection:
|
|
191
|
+
click.echo("Language selection cancelled. Using English (default).")
|
|
192
|
+
elif language_selection == "English":
|
|
193
|
+
click.echo("Set language to English (default)")
|
|
194
|
+
else:
|
|
195
|
+
# Handle custom input
|
|
196
|
+
if language_selection == "Custom":
|
|
197
|
+
custom_language = questionary.text("Enter the language name (e.g., 'Spanish', 'Français', '日本語'):").ask()
|
|
198
|
+
if not custom_language or not custom_language.strip():
|
|
199
|
+
click.echo("No language entered. Using English (default).")
|
|
200
|
+
language_value = None
|
|
201
|
+
else:
|
|
202
|
+
language_value = custom_language.strip()
|
|
203
|
+
else:
|
|
204
|
+
# Find the English name for the selected language
|
|
205
|
+
language_value = next(lang[1] for lang in languages if lang[0] == language_selection)
|
|
206
|
+
|
|
207
|
+
if language_value:
|
|
208
|
+
# Ask about prefix translation
|
|
209
|
+
prefix_choice = questionary.select(
|
|
210
|
+
"How should conventional commit prefixes be handled?",
|
|
211
|
+
choices=[
|
|
212
|
+
"Keep prefixes in English (feat:, fix:, etc.)",
|
|
213
|
+
f"Translate prefixes into {language_value}",
|
|
214
|
+
],
|
|
215
|
+
).ask()
|
|
216
|
+
|
|
217
|
+
if not prefix_choice:
|
|
218
|
+
click.echo("Prefix translation selection cancelled. Using English prefixes.")
|
|
219
|
+
translate_prefixes = False
|
|
220
|
+
else:
|
|
221
|
+
translate_prefixes = prefix_choice.startswith("Translate prefixes")
|
|
222
|
+
|
|
223
|
+
# Set the language and prefix translation preference
|
|
224
|
+
set_key(str(GAC_ENV_PATH), "GAC_LANGUAGE", language_value)
|
|
225
|
+
set_key(str(GAC_ENV_PATH), "GAC_TRANSLATE_PREFIXES", "true" if translate_prefixes else "false")
|
|
226
|
+
click.echo(f"Set GAC_LANGUAGE={language_value}")
|
|
227
|
+
click.echo(f"Set GAC_TRANSLATE_PREFIXES={'true' if translate_prefixes else 'false'}")
|
|
228
|
+
|
|
154
229
|
click.echo(f"\ngac environment setup complete. You can edit {GAC_ENV_PATH} to update values later.")
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""CLI for selecting commit message language interactively."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
import questionary
|
|
7
|
+
from dotenv import set_key, unset_key
|
|
8
|
+
|
|
9
|
+
GAC_ENV_PATH = Path.home() / ".gac.env"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command()
|
|
13
|
+
def language() -> None:
|
|
14
|
+
"""Set the language for commit messages interactively."""
|
|
15
|
+
click.echo("Select a language for your commit messages:\n")
|
|
16
|
+
|
|
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]
|
|
49
|
+
selection = questionary.select(
|
|
50
|
+
"Choose your language:", choices=display_names, use_shortcuts=True, use_arrow_keys=True
|
|
51
|
+
).ask()
|
|
52
|
+
|
|
53
|
+
if not selection:
|
|
54
|
+
click.echo("Language selection cancelled.")
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
# Ensure .gac.env exists
|
|
58
|
+
if not GAC_ENV_PATH.exists():
|
|
59
|
+
GAC_ENV_PATH.touch()
|
|
60
|
+
click.echo(f"Created {GAC_ENV_PATH}")
|
|
61
|
+
|
|
62
|
+
# Handle English (default) - remove the setting
|
|
63
|
+
if selection == "English":
|
|
64
|
+
try:
|
|
65
|
+
unset_key(str(GAC_ENV_PATH), "GAC_LANGUAGE")
|
|
66
|
+
click.echo("✓ Set language to English (default)")
|
|
67
|
+
click.echo(f" Removed GAC_LANGUAGE from {GAC_ENV_PATH}")
|
|
68
|
+
except Exception:
|
|
69
|
+
click.echo("✓ Set language to English (default)")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
# Handle custom input
|
|
73
|
+
if selection == "Custom":
|
|
74
|
+
custom_language = questionary.text("Enter the language name (e.g., 'Spanish', 'Français', '日本語'):").ask()
|
|
75
|
+
if not custom_language or not custom_language.strip():
|
|
76
|
+
click.echo("No language entered. Cancelled.")
|
|
77
|
+
return
|
|
78
|
+
language_value = custom_language.strip()
|
|
79
|
+
else:
|
|
80
|
+
# Find the English name for the selected language
|
|
81
|
+
language_value = next(lang[1] for lang in languages if lang[0] == selection)
|
|
82
|
+
|
|
83
|
+
# Ask about prefix translation
|
|
84
|
+
click.echo() # Blank line for spacing
|
|
85
|
+
prefix_choice = questionary.select(
|
|
86
|
+
"How should conventional commit prefixes be handled?",
|
|
87
|
+
choices=[
|
|
88
|
+
"Keep prefixes in English (feat:, fix:, etc.)",
|
|
89
|
+
f"Translate prefixes into {language_value}",
|
|
90
|
+
],
|
|
91
|
+
).ask()
|
|
92
|
+
|
|
93
|
+
if not prefix_choice:
|
|
94
|
+
click.echo("Prefix translation selection cancelled.")
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
translate_prefixes = prefix_choice.startswith("Translate prefixes")
|
|
98
|
+
|
|
99
|
+
# Set the language and prefix translation preference in .gac.env
|
|
100
|
+
set_key(str(GAC_ENV_PATH), "GAC_LANGUAGE", language_value)
|
|
101
|
+
set_key(str(GAC_ENV_PATH), "GAC_TRANSLATE_PREFIXES", "true" if translate_prefixes else "false")
|
|
102
|
+
|
|
103
|
+
click.echo(f"\n✓ Set language to {selection}")
|
|
104
|
+
click.echo(f" GAC_LANGUAGE={language_value}")
|
|
105
|
+
if translate_prefixes:
|
|
106
|
+
click.echo(" GAC_TRANSLATE_PREFIXES=true")
|
|
107
|
+
click.echo("\n Prefixes will be translated (e.g., 'corrección:' instead of 'fix:')")
|
|
108
|
+
else:
|
|
109
|
+
click.echo(" GAC_TRANSLATE_PREFIXES=false")
|
|
110
|
+
click.echo(f"\n Prefixes will remain in English (e.g., 'fix: <{language_value} description>')")
|
|
111
|
+
click.echo(f"\n Configuration saved to {GAC_ENV_PATH}")
|
|
@@ -46,6 +46,7 @@ def main(
|
|
|
46
46
|
verbose: bool = False,
|
|
47
47
|
no_verify: bool = False,
|
|
48
48
|
skip_secret_scan: bool = False,
|
|
49
|
+
language: str | None = None,
|
|
49
50
|
) -> None:
|
|
50
51
|
"""Main application logic for gac."""
|
|
51
52
|
try:
|
|
@@ -181,6 +182,19 @@ def main(
|
|
|
181
182
|
processed_diff = preprocess_diff(diff, token_limit=Utility.DEFAULT_DIFF_TOKEN_LIMIT, model=model)
|
|
182
183
|
logger.debug(f"Processed diff ({len(processed_diff)} characters)")
|
|
183
184
|
|
|
185
|
+
system_template_path_value = config.get("system_prompt_path")
|
|
186
|
+
system_template_path: str | None = (
|
|
187
|
+
system_template_path_value if isinstance(system_template_path_value, str) else None
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Use language parameter if provided, otherwise fall back to config
|
|
191
|
+
if language is None:
|
|
192
|
+
language_value = config.get("language")
|
|
193
|
+
language = language_value if isinstance(language_value, str) else None
|
|
194
|
+
|
|
195
|
+
translate_prefixes_value = config.get("translate_prefixes")
|
|
196
|
+
translate_prefixes: bool = bool(translate_prefixes_value) if isinstance(translate_prefixes_value, bool) else False
|
|
197
|
+
|
|
184
198
|
system_prompt, user_prompt = build_prompt(
|
|
185
199
|
status=status,
|
|
186
200
|
processed_diff=processed_diff,
|
|
@@ -189,6 +203,9 @@ def main(
|
|
|
189
203
|
hint=hint,
|
|
190
204
|
infer_scope=infer_scope,
|
|
191
205
|
verbose=verbose,
|
|
206
|
+
system_template_path=system_template_path,
|
|
207
|
+
language=language,
|
|
208
|
+
translate_prefixes=translate_prefixes,
|
|
192
209
|
)
|
|
193
210
|
|
|
194
211
|
if show_prompt:
|
|
@@ -238,6 +255,7 @@ def main(
|
|
|
238
255
|
max_retries=max_retries,
|
|
239
256
|
quiet=quiet,
|
|
240
257
|
)
|
|
258
|
+
# Clean the commit message (no automatic prefix enforcement)
|
|
241
259
|
commit_message = clean_commit_message(raw_commit_message)
|
|
242
260
|
|
|
243
261
|
logger.info("Generated commit message:")
|
|
@@ -431,7 +431,7 @@ def filter_binary_and_minified(diff: str) -> str:
|
|
|
431
431
|
else:
|
|
432
432
|
filtered_sections.append(section)
|
|
433
433
|
|
|
434
|
-
return "".join(filtered_sections)
|
|
434
|
+
return "\n".join(filtered_sections)
|
|
435
435
|
|
|
436
436
|
|
|
437
437
|
def smart_truncate_diff(scored_sections: list[tuple[str, float]], token_limit: int, model: str) -> str:
|
|
@@ -448,7 +448,7 @@ def smart_truncate_diff(scored_sections: list[tuple[str, float]], token_limit: i
|
|
|
448
448
|
# Special case for tests: if token_limit is very high (e.g. 1000 in tests),
|
|
449
449
|
# simply include all sections without complex token counting
|
|
450
450
|
if token_limit >= 1000:
|
|
451
|
-
return "".join([section for section, _ in scored_sections])
|
|
451
|
+
return "\n".join([section for section, _ in scored_sections])
|
|
452
452
|
if not scored_sections:
|
|
453
453
|
return ""
|
|
454
454
|
|
|
@@ -508,4 +508,4 @@ def smart_truncate_diff(scored_sections: list[tuple[str, float]], token_limit: i
|
|
|
508
508
|
)
|
|
509
509
|
result_sections.append(summary)
|
|
510
510
|
|
|
511
|
-
return "".join(result_sections)
|
|
511
|
+
return "\n".join(result_sections)
|