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.

Files changed (42) hide show
  1. {gac-1.13.1 → gac-1.15.0}/PKG-INFO +13 -1
  2. {gac-1.13.1 → gac-1.15.0}/README.md +12 -0
  3. {gac-1.13.1 → gac-1.15.0}/src/gac/__version__.py +1 -1
  4. {gac-1.13.1 → gac-1.15.0}/src/gac/cli.py +12 -1
  5. {gac-1.13.1 → gac-1.15.0}/src/gac/config.py +3 -0
  6. {gac-1.13.1 → gac-1.15.0}/src/gac/constants.py +136 -0
  7. {gac-1.13.1 → gac-1.15.0}/src/gac/init_cli.py +75 -0
  8. gac-1.15.0/src/gac/language_cli.py +111 -0
  9. {gac-1.13.1 → gac-1.15.0}/src/gac/main.py +18 -0
  10. {gac-1.13.1 → gac-1.15.0}/src/gac/preprocess.py +3 -3
  11. {gac-1.13.1 → gac-1.15.0}/src/gac/prompt.py +347 -229
  12. {gac-1.13.1 → gac-1.15.0}/.gitignore +0 -0
  13. {gac-1.13.1 → gac-1.15.0}/LICENSE +0 -0
  14. {gac-1.13.1 → gac-1.15.0}/pyproject.toml +0 -0
  15. {gac-1.13.1 → gac-1.15.0}/src/gac/__init__.py +0 -0
  16. {gac-1.13.1 → gac-1.15.0}/src/gac/ai.py +0 -0
  17. {gac-1.13.1 → gac-1.15.0}/src/gac/ai_utils.py +0 -0
  18. {gac-1.13.1 → gac-1.15.0}/src/gac/config_cli.py +0 -0
  19. {gac-1.13.1 → gac-1.15.0}/src/gac/diff_cli.py +0 -0
  20. {gac-1.13.1 → gac-1.15.0}/src/gac/errors.py +0 -0
  21. {gac-1.13.1 → gac-1.15.0}/src/gac/git.py +0 -0
  22. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/__init__.py +0 -0
  23. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/anthropic.py +0 -0
  24. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/cerebras.py +0 -0
  25. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/chutes.py +0 -0
  26. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/custom_anthropic.py +0 -0
  27. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/custom_openai.py +0 -0
  28. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/deepseek.py +0 -0
  29. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/fireworks.py +0 -0
  30. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/gemini.py +0 -0
  31. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/groq.py +0 -0
  32. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/lmstudio.py +0 -0
  33. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/minimax.py +0 -0
  34. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/ollama.py +0 -0
  35. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/openai.py +0 -0
  36. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/openrouter.py +0 -0
  37. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/streamlake.py +0 -0
  38. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/synthetic.py +0 -0
  39. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/together.py +0 -0
  40. {gac-1.13.1 → gac-1.15.0}/src/gac/providers/zai.py +0 -0
  41. {gac-1.13.1 → gac-1.15.0}/src/gac/security.py +0 -0
  42. {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.13.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
 
@@ -1,3 +1,3 @@
1
1
  """Version information for gac package."""
2
2
 
3
- __version__ = "1.13.1"
3
+ __version__ = "1.15.0"
@@ -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)