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.
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/PKG-INFO +11 -5
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/README.md +10 -4
- ace_git_copilot-0.6.2/ace/__init__.py +1 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/changelog_generator.py +5 -2
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/code_reviewer.py +10 -3
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/commit_generator.py +11 -9
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/conflict_resolver.py +5 -1
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/gitignore_generator.py +11 -4
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/history_analyzer.py +39 -12
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/intent_parser.py +10 -4
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/llm_factory.py +17 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/pr_drafter.py +6 -3
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/rebase_helper.py +18 -7
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/cli.py +100 -95
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/config.py +15 -13
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/git_ops.py +36 -20
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/dashboard.py +13 -6
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/conflict_parser.py +4 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/i18n.py +17 -0
- ace_git_copilot-0.6.2/ace/utils/json_utils.py +39 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/string_utils.py +2 -2
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/pyproject.toml +1 -1
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier3_combinations.py +1 -1
- ace_git_copilot-0.6.0/ace/__init__.py +0 -1
- ace_git_copilot-0.6.0/ace/utils/json_utils.py +0 -34
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/AGENTS.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/SCOPE.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/e2e_testing_track/progress.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/emojis_list.txt +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/find_unused_modules.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/handoff.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/measure_lazy_startup.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/measure_startup.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/profile_imports.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/progress.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/run_importtime.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_banner.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_emojis.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_git_usages.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/search_usages.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/test_import_profiler.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/explorer_init/test_mocked_sys.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/handoff.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/explorer_initial_report.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/implementation_track/progress.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/.gitkeep +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/PROJECT.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/orchestrator/progress.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/handoff.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/teamwork_preview_explorer_e2e_explore/progress.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_e2e_testing/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_e2e_testing/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_e2e_testing/progress.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_m1_startup/BRIEFING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_m1_startup/ORIGINAL_REQUEST.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.agents/worker_m1_startup/progress.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.env.example +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.github/workflows/tests.yml +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/.gitignore +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/CODE_OF_CONDUCT.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/CONTRIBUTING.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/LICENSE +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/PROJECT.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/SECURITY.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/SUPPORT.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/TEST_INFRA.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/TEST_READY.md +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/__main__.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/blame.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/changelog.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/commit.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/conflict.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/doctor.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/explain.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/ignore.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/intent.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/pr.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/rebase.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/review.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/search.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/standup.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/prompts/undo.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ai/push_resolver.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/context.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/diagnostics.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/hooks.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/core/safety.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/banner.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/display.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/prompts.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/ui/themes.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/ace/utils/diff_parser.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/media/cli_in_action.svg +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/media/dashboard.svg +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/media/stats.svg +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/conftest.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/conftest.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier1_features.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier2_boundaries.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/e2e/test_tier4_workloads.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_alias.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_blame.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_changelog_generator.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_code_reviewer.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_config_atomic_write.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_conflict_resolver.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_conflict_resolver_backup.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_context_gitdir_detection.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_diagnostics.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_diff_trimmer.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_fallback.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_git_ops.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_git_ops_execute.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_help.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_history_analyzer.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_hooks.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_i18n.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_ignore.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_intent_parser.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_interceptor.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_llm_factory.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_llm_factory_ollama.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_merge_state.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_pr_drafter.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_push_resolver.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_rebase_helper.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_safety.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_search.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_standup.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_string_utils.py +0 -0
- {ace_git_copilot-0.6.0 → ace_git_copilot-0.6.2}/tests/test_undo.py +0 -0
- {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.
|
|
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.
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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.
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
188
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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 = {
|