ace-git-copilot 0.6.0__tar.gz → 0.6.2__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.
Files changed (144) hide show
  1. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/PKG-INFO +11 -5
  2. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/README.md +10 -4
  3. ace_git_copilot-0.6.2/ace/__init__.py +1 -0
  4. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/changelog_generator.py +5 -2
  5. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/code_reviewer.py +10 -3
  6. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/commit_generator.py +11 -9
  7. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/conflict_resolver.py +5 -1
  8. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/gitignore_generator.py +11 -4
  9. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/history_analyzer.py +39 -12
  10. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/intent_parser.py +10 -4
  11. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/llm_factory.py +17 -0
  12. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/pr_drafter.py +6 -3
  13. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/rebase_helper.py +18 -7
  14. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/cli.py +100 -95
  15. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/config.py +15 -13
  16. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/git_ops.py +36 -20
  17. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/dashboard.py +13 -6
  18. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/conflict_parser.py +4 -0
  19. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/i18n.py +17 -0
  20. ace_git_copilot-0.6.2/ace/utils/json_utils.py +39 -0
  21. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/string_utils.py +2 -2
  22. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/pyproject.toml +1 -1
  23. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier3_combinations.py +1 -1
  24. ace_git_copilot-0.6.0/ace/__init__.py +0 -1
  25. ace_git_copilot-0.6.0/ace/utils/json_utils.py +0 -34
  26. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/AGENTS.md +0 -0
  27. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/BRIEFING.md +0 -0
  28. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/ORIGINAL_REQUEST.md +0 -0
  29. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/BRIEFING.md +0 -0
  30. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/ORIGINAL_REQUEST.md +0 -0
  31. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/SCOPE.md +0 -0
  32. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/progress.md +0 -0
  33. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/BRIEFING.md +0 -0
  34. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/ORIGINAL_REQUEST.md +0 -0
  35. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/emojis_list.txt +0 -0
  36. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/find_unused_modules.py +0 -0
  37. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/handoff.md +0 -0
  38. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/measure_lazy_startup.py +0 -0
  39. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/measure_startup.py +0 -0
  40. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/profile_imports.py +0 -0
  41. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/progress.md +0 -0
  42. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/run_importtime.py +0 -0
  43. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_banner.py +0 -0
  44. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_emojis.py +0 -0
  45. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_git_usages.py +0 -0
  46. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_usages.py +0 -0
  47. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/test_import_profiler.py +0 -0
  48. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/test_mocked_sys.py +0 -0
  49. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/handoff.md +0 -0
  50. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/BRIEFING.md +0 -0
  51. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/ORIGINAL_REQUEST.md +0 -0
  52. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/explorer_initial_report.md +0 -0
  53. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/progress.md +0 -0
  54. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/.gitkeep +0 -0
  55. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/BRIEFING.md +0 -0
  56. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/ORIGINAL_REQUEST.md +0 -0
  57. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/PROJECT.md +0 -0
  58. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/progress.md +0 -0
  59. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/BRIEFING.md +0 -0
  60. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/ORIGINAL_REQUEST.md +0 -0
  61. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/handoff.md +0 -0
  62. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/progress.md +0 -0
  63. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_e2e_testing/BRIEFING.md +0 -0
  64. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_e2e_testing/ORIGINAL_REQUEST.md +0 -0
  65. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_e2e_testing/progress.md +0 -0
  66. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_m1_startup/BRIEFING.md +0 -0
  67. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_m1_startup/ORIGINAL_REQUEST.md +0 -0
  68. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_m1_startup/progress.md +0 -0
  69. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.env.example +0 -0
  70. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.github/workflows/tests.yml +0 -0
  71. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.gitignore +0 -0
  72. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/CODE_OF_CONDUCT.md +0 -0
  73. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/CONTRIBUTING.md +0 -0
  74. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/LICENSE +0 -0
  75. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/PROJECT.md +0 -0
  76. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/SECURITY.md +0 -0
  77. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/SUPPORT.md +0 -0
  78. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/TEST_INFRA.md +0 -0
  79. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/TEST_READY.md +0 -0
  80. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/__main__.py +0 -0
  81. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/blame.py +0 -0
  82. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/changelog.py +0 -0
  83. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/commit.py +0 -0
  84. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/conflict.py +0 -0
  85. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/doctor.py +0 -0
  86. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/explain.py +0 -0
  87. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/ignore.py +0 -0
  88. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/intent.py +0 -0
  89. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/pr.py +0 -0
  90. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/rebase.py +0 -0
  91. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/review.py +0 -0
  92. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/search.py +0 -0
  93. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/standup.py +0 -0
  94. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/undo.py +0 -0
  95. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/push_resolver.py +0 -0
  96. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/context.py +0 -0
  97. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/diagnostics.py +0 -0
  98. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/hooks.py +0 -0
  99. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/safety.py +0 -0
  100. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/banner.py +0 -0
  101. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/display.py +0 -0
  102. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/prompts.py +0 -0
  103. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/themes.py +0 -0
  104. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/diff_parser.py +0 -0
  105. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/media/cli_in_action.svg +0 -0
  106. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/media/dashboard.svg +0 -0
  107. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/media/stats.svg +0 -0
  108. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/conftest.py +0 -0
  109. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/conftest.py +0 -0
  110. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier1_features.py +0 -0
  111. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier2_boundaries.py +0 -0
  112. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier4_workloads.py +0 -0
  113. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_alias.py +0 -0
  114. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_blame.py +0 -0
  115. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_changelog_generator.py +0 -0
  116. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_code_reviewer.py +0 -0
  117. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_config_atomic_write.py +0 -0
  118. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_conflict_resolver.py +0 -0
  119. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_conflict_resolver_backup.py +0 -0
  120. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_context_gitdir_detection.py +0 -0
  121. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_diagnostics.py +0 -0
  122. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_diff_trimmer.py +0 -0
  123. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_fallback.py +0 -0
  124. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_git_ops.py +0 -0
  125. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_git_ops_execute.py +0 -0
  126. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_help.py +0 -0
  127. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_history_analyzer.py +0 -0
  128. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_hooks.py +0 -0
  129. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_i18n.py +0 -0
  130. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_ignore.py +0 -0
  131. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_intent_parser.py +0 -0
  132. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_interceptor.py +0 -0
  133. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_llm_factory.py +0 -0
  134. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_llm_factory_ollama.py +0 -0
  135. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_merge_state.py +0 -0
  136. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_pr_drafter.py +0 -0
  137. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_push_resolver.py +0 -0
  138. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_rebase_helper.py +0 -0
  139. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_safety.py +0 -0
  140. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_search.py +0 -0
  141. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_standup.py +0 -0
  142. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_string_utils.py +0 -0
  143. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_undo.py +0 -0
  144. {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ace-git-copilot
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: AI-powered Git copilot — talk to Git in plain English
5
5
  Project-URL: Homepage, https://github.com/jachinsamuel/Ace
6
6
  Project-URL: Documentation, https://github.com/jachinsamuel/Ace#readme
@@ -59,6 +59,7 @@ Ace is an intelligent command-line tool that brings AI assistance directly to yo
59
59
  ## Features
60
60
 
61
61
  * **Natural Language Git Commands**: Translate commands like "undo my last commit but keep changes" or "create a branch login-page" into clean, standard Git actions.
62
+ * **Multi-Language / i18n Output**: Native multi-language support. Set `ACE_LANGUAGE="zh-CN"` or configure output language in `ace setup` to get all AI reviews, commit explanations, and diagnostics in your native language (Simplified/Traditional Chinese, Spanish, French, German, Italian, Portuguese, Japanese, Korean, Russian, Hindi, and more).
62
63
  * **AI-Powered Commits**: Analyzes staged diffs and generates Conventional Commit messages automatically.
63
64
  * **Automated Code Review**: Rates code quality and points out bugs, security flaws, or styling issues inside staged or unstaged diffs.
64
65
  * **Merge Conflict Resolution**: Interactively walks you through conflict blocks and suggests correct merges.
@@ -108,7 +109,7 @@ For Python command-line applications like Ace, it is highly recommended to insta
108
109
 
109
110
  ## Configuration and Setup
110
111
 
111
- Run the built-in configuration wizard to select your AI model provider:
112
+ Run the built-in configuration wizard to select your AI model provider and target output language:
112
113
  ```bash
113
114
  ace setup
114
115
  ```
@@ -125,17 +126,22 @@ Uses cloud-hosted high-performance models.
125
126
  * You will need an NVIDIA developer API key. Get one for free at [NVIDIA build](https://build.nvidia.com/).
126
127
  * Default model: `meta/llama-3.3-70b-instruct`
127
128
 
128
- ### 3. OpenAI
129
+ ### 3. Google / Gemini
130
+ Uses Google Gemini models.
131
+ * You will need a Google AI API key set as `GOOGLE_API_KEY` or `GEMINI_API_KEY`.
132
+ * Default model: `gemini-1.5-flash`
133
+
134
+ ### 4. OpenAI
129
135
  Uses OpenAI GPT models.
130
136
  * You will need an OpenAI API key.
131
137
  * Default model: `gpt-4o-mini`
132
138
 
133
- ### 4. Anthropic
139
+ ### 5. Anthropic
134
140
  Uses Anthropic Claude models.
135
141
  * You will need an Anthropic API key.
136
142
  * Default model: `claude-3-5-sonnet-latest`
137
143
 
138
- ### 5. Custom OpenAI-Compatible
144
+ ### 6. Custom OpenAI-Compatible
139
145
  Allows using any custom endpoint that supports the OpenAI API schema (e.g. Groq, OpenRouter, Together AI).
140
146
  * You will need the provider's API key and custom API base URL (e.g. `https://api.groq.com/openai/v1`).
141
147
  * Default model: `custom-model`
@@ -26,6 +26,7 @@ Ace is an intelligent command-line tool that brings AI assistance directly to yo
26
26
  ## Features
27
27
 
28
28
  * **Natural Language Git Commands**: Translate commands like "undo my last commit but keep changes" or "create a branch login-page" into clean, standard Git actions.
29
+ * **Multi-Language / i18n Output**: Native multi-language support. Set `ACE_LANGUAGE="zh-CN"` or configure output language in `ace setup` to get all AI reviews, commit explanations, and diagnostics in your native language (Simplified/Traditional Chinese, Spanish, French, German, Italian, Portuguese, Japanese, Korean, Russian, Hindi, and more).
29
30
  * **AI-Powered Commits**: Analyzes staged diffs and generates Conventional Commit messages automatically.
30
31
  * **Automated Code Review**: Rates code quality and points out bugs, security flaws, or styling issues inside staged or unstaged diffs.
31
32
  * **Merge Conflict Resolution**: Interactively walks you through conflict blocks and suggests correct merges.
@@ -75,7 +76,7 @@ For Python command-line applications like Ace, it is highly recommended to insta
75
76
 
76
77
  ## Configuration and Setup
77
78
 
78
- Run the built-in configuration wizard to select your AI model provider:
79
+ Run the built-in configuration wizard to select your AI model provider and target output language:
79
80
  ```bash
80
81
  ace setup
81
82
  ```
@@ -92,17 +93,22 @@ Uses cloud-hosted high-performance models.
92
93
  * You will need an NVIDIA developer API key. Get one for free at [NVIDIA build](https://build.nvidia.com/).
93
94
  * Default model: `meta/llama-3.3-70b-instruct`
94
95
 
95
- ### 3. OpenAI
96
+ ### 3. Google / Gemini
97
+ Uses Google Gemini models.
98
+ * You will need a Google AI API key set as `GOOGLE_API_KEY` or `GEMINI_API_KEY`.
99
+ * Default model: `gemini-1.5-flash`
100
+
101
+ ### 4. OpenAI
96
102
  Uses OpenAI GPT models.
97
103
  * You will need an OpenAI API key.
98
104
  * Default model: `gpt-4o-mini`
99
105
 
100
- ### 4. Anthropic
106
+ ### 5. Anthropic
101
107
  Uses Anthropic Claude models.
102
108
  * You will need an Anthropic API key.
103
109
  * Default model: `claude-3-5-sonnet-latest`
104
110
 
105
- ### 5. Custom OpenAI-Compatible
111
+ ### 6. Custom OpenAI-Compatible
106
112
  Allows using any custom endpoint that supports the OpenAI API schema (e.g. Groq, OpenRouter, Together AI).
107
113
  * You will need the provider's API key and custom API base URL (e.g. `https://api.groq.com/openai/v1`).
108
114
  * Default model: `custom-model`
@@ -0,0 +1 @@
1
+ __version__ = "0.6.2"
@@ -89,5 +89,8 @@ class ChangelogGenerator:
89
89
  HumanMessage(content=user_prompt)
90
90
  ]
91
91
 
92
- response = llm.invoke(messages)
93
- return response.content.strip()
92
+ try:
93
+ response = llm.invoke(messages)
94
+ return response.content.strip()
95
+ except Exception as e:
96
+ return f"Failed to generate changelog: {e}"
@@ -56,11 +56,18 @@ class CodeReviewer:
56
56
  response = llm.invoke(messages)
57
57
  parsed = extract_json(response.content)
58
58
 
59
- # Extract score and findings
60
- score = float(parsed.get("score", 10.0))
59
+ raw_score = parsed.get("score") if isinstance(parsed, dict) else 10.0
60
+ if raw_score is None:
61
+ score = 10.0
62
+ else:
63
+ try:
64
+ score_str = str(raw_score).split("/")[0].strip()
65
+ score = float(score_str)
66
+ except (ValueError, TypeError):
67
+ score = 10.0
61
68
  scores.append(score)
62
69
 
63
- findings = parsed.get("findings", [])
70
+ findings = parsed.get("findings", []) if isinstance(parsed, dict) else []
64
71
  if isinstance(findings, list):
65
72
  for finding in findings:
66
73
  # Annotate with the filename
@@ -68,16 +68,18 @@ class CommitGenerator:
68
68
 
69
69
  # Get LLM and run inference
70
70
  llm = get_llm(offline_override=offline)
71
- response = llm.invoke(messages)
71
+ try:
72
+ response = llm.invoke(messages)
73
+ message = response.content.strip()
74
+ except Exception as e:
75
+ raise Exception(f"AI commit message generation failed: {e}")
72
76
 
73
77
  # Clean response (remove extra leading/trailing whitespace or markdown fences)
74
- message = response.content.strip()
75
- if message.startswith("```"):
76
- lines = message.splitlines()
77
- if lines[0].startswith("```"):
78
- lines = lines[1:]
79
- if lines and lines[-1].startswith("```"):
80
- lines = lines[:-1]
81
- message = "\n".join(lines).strip()
78
+ import re
79
+ match = re.search(r"```(?:gitcommit|text)?\s*(.*?)\s*```", message, re.DOTALL)
80
+ if match:
81
+ message = match.group(1).strip()
82
+ else:
83
+ message = message.replace("```", "").strip()
82
84
 
83
85
  return message
@@ -46,8 +46,12 @@ class ConflictResolver:
46
46
  llm = get_llm(offline_override=offline)
47
47
  suggestions = []
48
48
 
49
+ from ace.core.config import get_config
50
+ from ace.utils.i18n import get_language_instruction
51
+ lang_inst = get_language_instruction(get_config().ai.language)
52
+
49
53
  for block in blocks:
50
- sys_prompt = CONFLICT_SYSTEM_PROMPT.format(filename=file_path)
54
+ sys_prompt = CONFLICT_SYSTEM_PROMPT.format(filename=file_path) + lang_inst
51
55
  usr_prompt = USER_PROMPT_TEMPLATE.format(
52
56
  head_content=block["head"],
53
57
  incoming_content=block["incoming"]
@@ -31,15 +31,22 @@ class GitignoreGenerator:
31
31
  current_gitignore=current_content.strip() or "(empty)"
32
32
  )
33
33
 
34
+ from ace.core.config import get_config
35
+ from ace.utils.i18n import get_language_instruction
36
+ lang_inst = get_language_instruction(get_config().ai.language)
37
+
34
38
  messages = [
35
- SystemMessage(content=IGNORE_SYSTEM_PROMPT),
39
+ SystemMessage(content=IGNORE_SYSTEM_PROMPT + lang_inst),
36
40
  HumanMessage(content=usr_prompt)
37
41
  ]
38
42
 
39
- response = llm.invoke(messages)
40
- parsed = extract_json(response.content)
43
+ try:
44
+ response = llm.invoke(messages)
45
+ parsed = extract_json(response.content)
46
+ except Exception as e:
47
+ raise Exception(f"AI gitignore generation failed: {e}")
41
48
 
42
- if "rules" not in parsed or "explanation" not in parsed:
49
+ if not isinstance(parsed, dict) or "rules" not in parsed or "explanation" not in parsed:
43
50
  raise Exception("AI response JSON missing 'rules' or 'explanation' keys.")
44
51
 
45
52
  return parsed
@@ -30,7 +30,11 @@ class HistoryAnalyzer:
30
30
 
31
31
  llm = get_llm(offline_override=offline)
32
32
 
33
- sys_prompt = HISTORY_SUMMARY_SYSTEM_PROMPT.format(query=query, command=command)
33
+ from ace.core.config import get_config
34
+ from ace.utils.i18n import get_language_instruction
35
+ lang_inst = get_language_instruction(get_config().ai.language)
36
+
37
+ sys_prompt = HISTORY_SUMMARY_SYSTEM_PROMPT.format(query=query, command=command) + lang_inst
34
38
  user_prompt = f"Raw Git Output:\n\"\"\"\n{command_output[:20000]}\n\"\"\""
35
39
 
36
40
  messages = [
@@ -39,8 +43,11 @@ class HistoryAnalyzer:
39
43
  ]
40
44
 
41
45
  from ace.utils.string_utils import strip_emojis
42
- response = llm.invoke(messages)
43
- return strip_emojis(response.content.strip())
46
+ try:
47
+ response = llm.invoke(messages)
48
+ return strip_emojis(response.content.strip())
49
+ except Exception as e:
50
+ return f"Failed to generate summary: {e}"
44
51
 
45
52
  def get_repo_stats(self) -> Dict[str, Any]:
46
53
  """
@@ -168,7 +175,10 @@ class HistoryAnalyzer:
168
175
 
169
176
  from ace.ai.prompts.search import SEARCH_SYSTEM_PROMPT, USER_PROMPT_TEMPLATE
170
177
  from ace.utils.json_utils import extract_json
178
+ from ace.core.config import get_config
179
+ from ace.utils.i18n import get_language_instruction
171
180
 
181
+ lang_inst = get_language_instruction(get_config().ai.language)
172
182
  llm = get_llm(offline_override=offline)
173
183
 
174
184
  if len(commit_history_text) > 25000:
@@ -180,12 +190,15 @@ class HistoryAnalyzer:
180
190
  )
181
191
 
182
192
  messages = [
183
- SystemMessage(content=SEARCH_SYSTEM_PROMPT),
193
+ SystemMessage(content=SEARCH_SYSTEM_PROMPT + lang_inst),
184
194
  HumanMessage(content=usr_prompt)
185
195
  ]
186
196
 
187
- response = llm.invoke(messages)
188
- return extract_json(response.content)
197
+ try:
198
+ response = llm.invoke(messages)
199
+ return extract_json(response.content)
200
+ except Exception:
201
+ return {"matches": []}
189
202
 
190
203
  def generate_standup(self, commits: list, offline: bool = False) -> str:
191
204
  """
@@ -201,16 +214,23 @@ class HistoryAnalyzer:
201
214
  commit_list_text = "\n".join(commit_lines)
202
215
 
203
216
  from ace.ai.prompts.standup import STANDUP_SYSTEM_PROMPT, USER_PROMPT_TEMPLATE
217
+ from ace.core.config import get_config
218
+ from ace.utils.i18n import get_language_instruction
219
+
220
+ lang_inst = get_language_instruction(get_config().ai.language)
204
221
  llm = get_llm(offline_override=offline)
205
222
 
206
223
  usr_prompt = USER_PROMPT_TEMPLATE.format(commit_list_text=commit_list_text)
207
224
  messages = [
208
- SystemMessage(content=STANDUP_SYSTEM_PROMPT),
225
+ SystemMessage(content=STANDUP_SYSTEM_PROMPT + lang_inst),
209
226
  HumanMessage(content=usr_prompt)
210
227
  ]
211
228
 
212
- response = llm.invoke(messages)
213
- return response.content.strip()
229
+ try:
230
+ response = llm.invoke(messages)
231
+ return response.content.strip()
232
+ except Exception as e:
233
+ return f"Failed to generate standup: {e}"
214
234
 
215
235
  def analyze_blame(
216
236
  self,
@@ -225,6 +245,10 @@ class HistoryAnalyzer:
225
245
  Analyze why a specific line was written using LLM and commit patch info.
226
246
  """
227
247
  from ace.ai.prompts.blame import BLAME_SYSTEM_PROMPT, USER_PROMPT_TEMPLATE
248
+ from ace.core.config import get_config
249
+ from ace.utils.i18n import get_language_instruction
250
+
251
+ lang_inst = get_language_instruction(get_config().ai.language)
228
252
  llm = get_llm(offline_override=offline)
229
253
 
230
254
  usr_prompt = USER_PROMPT_TEMPLATE.format(
@@ -240,10 +264,13 @@ class HistoryAnalyzer:
240
264
  )
241
265
 
242
266
  messages = [
243
- SystemMessage(content=BLAME_SYSTEM_PROMPT),
267
+ SystemMessage(content=BLAME_SYSTEM_PROMPT + lang_inst),
244
268
  HumanMessage(content=usr_prompt)
245
269
  ]
246
270
 
247
- response = llm.invoke(messages)
248
- return response.content.strip()
271
+ try:
272
+ response = llm.invoke(messages)
273
+ return response.content.strip()
274
+ except Exception as e:
275
+ return f"Failed to analyze line blame: {e}"
249
276
 
@@ -35,16 +35,22 @@ class IntentParser:
35
35
  query=query
36
36
  )
37
37
 
38
+ from ace.core.config import get_config
39
+ from ace.utils.i18n import get_language_instruction
40
+ lang_inst = get_language_instruction(get_config().ai.language)
41
+
38
42
  messages = [
39
- SystemMessage(content=INTENT_SYSTEM_PROMPT),
43
+ SystemMessage(content=INTENT_SYSTEM_PROMPT + lang_inst),
40
44
  HumanMessage(content=user_prompt)
41
45
  ]
42
46
 
43
47
  # Initialize and call LLM
44
48
  llm = get_llm(offline_override=offline)
45
- response = llm.invoke(messages)
46
-
47
- raw_content = response.content.strip()
49
+ try:
50
+ response = llm.invoke(messages)
51
+ raw_content = response.content.strip()
52
+ except Exception as e:
53
+ raise IntentParserError(f"AI intent parsing failed: {e}")
48
54
 
49
55
  # Parse the output using shared utility
50
56
  try:
@@ -234,6 +234,23 @@ def get_llm(offline_override: bool = False) -> BaseChatModel:
234
234
  max_tokens=2048,
235
235
  )
236
236
 
237
+ elif provider in ("google", "gemini"):
238
+ api_key = getattr(config.ai, "google_api_key", None) or os.getenv("GOOGLE_API_KEY") or os.getenv("GEMINI_API_KEY")
239
+ if not api_key:
240
+ primary_llm = DummyMissingKeyLLM(provider, "Google/Gemini API Key not found. Please set GOOGLE_API_KEY or GEMINI_API_KEY.")
241
+ else:
242
+ try:
243
+ from langchain_google_genai import ChatGoogleGenerativeAI
244
+ model_name = getattr(config.ai, "google_model", None) or "gemini-1.5-flash"
245
+ primary_llm = ChatGoogleGenerativeAI(
246
+ model=model_name,
247
+ google_api_key=api_key,
248
+ temperature=0.0,
249
+ max_output_tokens=2048,
250
+ )
251
+ except ImportError:
252
+ primary_llm = DummyMissingKeyLLM(provider, "langchain-google-genai is not installed. Run 'pip install langchain-google-genai'.")
253
+
237
254
  elif provider == "custom":
238
255
  api_key = config.ai.custom_api_key or os.getenv("CUSTOM_API_KEY")
239
256
  base_url = config.ai.custom_api_base or os.getenv("CUSTOM_API_BASE")
@@ -57,10 +57,13 @@ class PRDrafter:
57
57
  HumanMessage(content=usr_prompt)
58
58
  ]
59
59
 
60
- response = llm.invoke(messages)
61
- parsed = extract_json(response.content)
60
+ try:
61
+ response = llm.invoke(messages)
62
+ parsed = extract_json(response.content)
63
+ except Exception as e:
64
+ raise Exception(f"AI PR drafting failed: {e}")
62
65
 
63
- if "title" not in parsed or "body" not in parsed:
66
+ if not isinstance(parsed, dict) or "title" not in parsed or "body" not in parsed:
64
67
  raise Exception("AI response JSON missing 'title' or 'body' keys.")
65
68
 
66
69
  return parsed
@@ -40,27 +40,38 @@ class RebaseHelper:
40
40
  commit_lines.append(f"- {c['hexsha'][:7]} - {c['summary']} (by {c['author']})")
41
41
  commit_list_text = "\n".join(commit_lines)
42
42
 
43
+ from ace.core.config import get_config
44
+ from ace.utils.i18n import get_language_instruction
45
+ lang_inst = get_language_instruction(get_config().ai.language)
46
+
43
47
  usr_prompt = USER_PROMPT_TEMPLATE.format(commit_list_text=commit_list_text)
44
48
 
45
49
  messages = [
46
- SystemMessage(content=REBASE_SYSTEM_PROMPT),
50
+ SystemMessage(content=REBASE_SYSTEM_PROMPT + lang_inst),
47
51
  HumanMessage(content=usr_prompt)
48
52
  ]
49
53
 
50
54
  llm = get_llm(offline_override=offline)
51
- response = llm.invoke(messages)
52
-
53
- return extract_json(response.content)
55
+ try:
56
+ response = llm.invoke(messages)
57
+ return extract_json(response.content)
58
+ except Exception as e:
59
+ return {"recommendations": [], "summary": f"Failed to analyze commits: {e}"}
54
60
 
55
61
  def run_auto_rebase(self, base_branch: str, recommendations: List[Dict[str, Any]]) -> str:
56
62
  """Execute programmatic interactive rebase using Python sequence and message editors."""
57
63
  action_map = {}
58
64
  reword_messages = {}
59
65
  for rec in recommendations:
66
+ if not isinstance(rec, dict) or "hexsha" not in rec:
67
+ continue
60
68
  sha = rec["hexsha"][:7].lower()
61
- action_map[sha] = rec["action"].lower()
62
- if rec["action"].lower() == "reword" and rec.get("new_message"):
63
- reword_messages[rec["summary"].strip()] = rec["new_message"]
69
+ action = rec.get("action", "pick").lower()
70
+ action_map[sha] = action
71
+ if action == "reword" and rec.get("new_message"):
72
+ summary = rec.get("summary", "").strip()
73
+ if summary:
74
+ reword_messages[summary] = rec["new_message"]
64
75
 
65
76
  # Create mapping JSON
66
77
  map_data = {