gitpr-cli 0.0.30__tar.gz → 0.0.31__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.
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/PKG-INFO +24 -1
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/README.md +23 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/gitpr_cli.egg-info/PKG-INFO +24 -1
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/gitpr_cli.egg-info/SOURCES.txt +1 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/pyproject.toml +1 -1
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/ai_providers.py +5 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/config.py +30 -2
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/core.py +18 -3
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/issue_engine.py +24 -4
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/main.py +41 -13
- gitpr_cli-0.0.31/src/metrics.py +588 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/spinner.py +6 -6
- gitpr_cli-0.0.31/src/tui_issue.py +122 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/ui/issue_app.py +16 -4
- gitpr_cli-0.0.31/src/ui/metrics_app.py +434 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/updater.py +2 -2
- gitpr_cli-0.0.31/tests/test_metrics.py +833 -0
- gitpr_cli-0.0.30/src/metrics.py +0 -266
- gitpr_cli-0.0.30/src/tui_issue.py +0 -37
- gitpr_cli-0.0.30/src/ui/metrics_app.py +0 -168
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/LICENSE +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/gitpr_cli.egg-info/dependency_links.txt +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/gitpr_cli.egg-info/entry_points.txt +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/gitpr_cli.egg-info/requires.txt +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/gitpr_cli.egg-info/top_level.txt +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/setup.cfg +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/__init__.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/blame_engine.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/cache.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/chat_memory.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/i18n.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/linter_engine.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/mcp_server.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/security.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/ui/__init__.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/ui/chat_app.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/src/ui/help_screen.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_chat_backend.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_core.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_install_wizard.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_mcp_prompts.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_mcp_server.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_pre_save.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_skill_command.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_smart_excludes.py +0 -0
- {gitpr_cli-0.0.30 → gitpr_cli-0.0.31}/tests/test_thinking_words.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitpr-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.31
|
|
4
4
|
Summary: AI-powered PR automation, commit messages, and code review (Gemini, DeepSeek, and Ollama)
|
|
5
5
|
Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -28,6 +28,29 @@ GitPR CLI is a command-line automation tool that uses **Google Gemini** and **De
|
|
|
28
28
|
|
|
29
29
|
🌐 **Website:** [gitpr.natanfiuza.dev.br](https://gitpr.natanfiuza.dev.br/) · 📂 **Repository:** [github.com/natanfiuza/gitpr](https://github.com/natanfiuza/gitpr)
|
|
30
30
|
|
|
31
|
+
----
|
|
32
|
+
|
|
33
|
+
## ⚡ **Quick Start**
|
|
34
|
+
|
|
35
|
+
### **1. Installation via PyPI**
|
|
36
|
+
|
|
37
|
+
Install GitPR CLI using `pip`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install gitpr-cli
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### **2. Initializing in a Repository**
|
|
44
|
+
|
|
45
|
+
To set up GitPR in a folder of a new repository, run:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
gitpr --install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Guided Setup:** Guided configuration that downloads skill templates, installs Git Hooks, configures MCP for your editors, and verifies the API key for your AI provider.
|
|
52
|
+
> 📖 **Full Documentation:** [https://gitpr.natanfiuza.dev.br/docs/install-wizard?lang=en_us](https://gitpr.natanfiuza.dev.br/docs/install-wizard?lang=en_us)
|
|
53
|
+
|
|
31
54
|
## **🛠️ Technologies and Libraries Used**
|
|
32
55
|
|
|
33
56
|
This project was developed in Python and uses the following main libraries:
|
|
@@ -9,6 +9,29 @@ GitPR CLI is a command-line automation tool that uses **Google Gemini** and **De
|
|
|
9
9
|
|
|
10
10
|
🌐 **Website:** [gitpr.natanfiuza.dev.br](https://gitpr.natanfiuza.dev.br/) · 📂 **Repository:** [github.com/natanfiuza/gitpr](https://github.com/natanfiuza/gitpr)
|
|
11
11
|
|
|
12
|
+
----
|
|
13
|
+
|
|
14
|
+
## ⚡ **Quick Start**
|
|
15
|
+
|
|
16
|
+
### **1. Installation via PyPI**
|
|
17
|
+
|
|
18
|
+
Install GitPR CLI using `pip`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install gitpr-cli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### **2. Initializing in a Repository**
|
|
25
|
+
|
|
26
|
+
To set up GitPR in a folder of a new repository, run:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
gitpr --install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> **Guided Setup:** Guided configuration that downloads skill templates, installs Git Hooks, configures MCP for your editors, and verifies the API key for your AI provider.
|
|
33
|
+
> 📖 **Full Documentation:** [https://gitpr.natanfiuza.dev.br/docs/install-wizard?lang=en_us](https://gitpr.natanfiuza.dev.br/docs/install-wizard?lang=en_us)
|
|
34
|
+
|
|
12
35
|
## **🛠️ Technologies and Libraries Used**
|
|
13
36
|
|
|
14
37
|
This project was developed in Python and uses the following main libraries:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitpr-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.31
|
|
4
4
|
Summary: AI-powered PR automation, commit messages, and code review (Gemini, DeepSeek, and Ollama)
|
|
5
5
|
Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -28,6 +28,29 @@ GitPR CLI is a command-line automation tool that uses **Google Gemini** and **De
|
|
|
28
28
|
|
|
29
29
|
🌐 **Website:** [gitpr.natanfiuza.dev.br](https://gitpr.natanfiuza.dev.br/) · 📂 **Repository:** [github.com/natanfiuza/gitpr](https://github.com/natanfiuza/gitpr)
|
|
30
30
|
|
|
31
|
+
----
|
|
32
|
+
|
|
33
|
+
## ⚡ **Quick Start**
|
|
34
|
+
|
|
35
|
+
### **1. Installation via PyPI**
|
|
36
|
+
|
|
37
|
+
Install GitPR CLI using `pip`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install gitpr-cli
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### **2. Initializing in a Repository**
|
|
44
|
+
|
|
45
|
+
To set up GitPR in a folder of a new repository, run:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
gitpr --install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Guided Setup:** Guided configuration that downloads skill templates, installs Git Hooks, configures MCP for your editors, and verifies the API key for your AI provider.
|
|
52
|
+
> 📖 **Full Documentation:** [https://gitpr.natanfiuza.dev.br/docs/install-wizard?lang=en_us](https://gitpr.natanfiuza.dev.br/docs/install-wizard?lang=en_us)
|
|
53
|
+
|
|
31
54
|
## **🛠️ Technologies and Libraries Used**
|
|
32
55
|
|
|
33
56
|
This project was developed in Python and uses the following main libraries:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitpr-cli"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.31"
|
|
8
8
|
description = "AI-powered PR automation, commit messages, and code review (Gemini, DeepSeek, and Ollama)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -73,6 +73,8 @@ def call_ai_model(provider, api_key, api_model, prompt, system_instruction, quie
|
|
|
73
73
|
spinner = Spinner(quiet=quiet)
|
|
74
74
|
spinner.start()
|
|
75
75
|
|
|
76
|
+
start_time = time.perf_counter()
|
|
77
|
+
|
|
76
78
|
try:
|
|
77
79
|
for attempt in range(1, max_retries + 1):
|
|
78
80
|
try:
|
|
@@ -135,6 +137,9 @@ def call_ai_model(provider, api_key, api_model, prompt, system_instruction, quie
|
|
|
135
137
|
result_json = result_json[0] if result_json else {}
|
|
136
138
|
|
|
137
139
|
# Inject telemetry metadata silently into the response dictionary
|
|
140
|
+
meta_raw["duration_ms"] = int((time.perf_counter() - start_time) * 1000)
|
|
141
|
+
meta_raw["provider"] = provider
|
|
142
|
+
meta_raw["model"] = api_model
|
|
138
143
|
result_json["_telemetry_meta"] = meta_raw
|
|
139
144
|
|
|
140
145
|
spinner.stop()
|
|
@@ -211,7 +211,35 @@ def get_github_token():
|
|
|
211
211
|
"""Reads and decrypts the GitHub Personal Access Token (PAT)."""
|
|
212
212
|
load_dotenv(ENV_FILE)
|
|
213
213
|
encrypted_token = os.getenv("GITHUB_TOKEN_ENCRYPTED")
|
|
214
|
-
|
|
214
|
+
|
|
215
215
|
if encrypted_token:
|
|
216
216
|
return decrypt_data(encrypted_token)
|
|
217
|
-
return None
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def validate_github_token(token):
|
|
221
|
+
"""
|
|
222
|
+
Validates a GitHub PAT by making a lightweight API call to /user.
|
|
223
|
+
Returns (is_valid: bool, error_message: str).
|
|
224
|
+
"""
|
|
225
|
+
import requests
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
headers = {
|
|
229
|
+
"Authorization": f"token {token}",
|
|
230
|
+
"Accept": "application/vnd.github.v3+json"
|
|
231
|
+
}
|
|
232
|
+
response = requests.get("https://api.github.com/user", headers=headers, timeout=10)
|
|
233
|
+
|
|
234
|
+
if response.status_code == 200:
|
|
235
|
+
return True, ""
|
|
236
|
+
elif response.status_code == 401:
|
|
237
|
+
return False, __("Token expired or invalid. Please generate a new one.")
|
|
238
|
+
else:
|
|
239
|
+
return False, __("Unexpected response from GitHub (HTTP {code})", code=response.status_code)
|
|
240
|
+
except requests.exceptions.ConnectionError:
|
|
241
|
+
return False, __("No internet connection. Cannot validate GitHub token.")
|
|
242
|
+
except requests.exceptions.Timeout:
|
|
243
|
+
return False, __("GitHub API timeout. Check your connection and try again.")
|
|
244
|
+
except Exception as e:
|
|
245
|
+
return False, __("Failed to validate token: {error}", error=str(e))
|
|
@@ -262,6 +262,8 @@ def generate_pr_content(action_folder, action_type, diff_text, provider="gemini"
|
|
|
262
262
|
click.secho(__("⚠️ No diff found. Make some changes before running the command."), fg="yellow")
|
|
263
263
|
return None
|
|
264
264
|
|
|
265
|
+
t_start = time.perf_counter()
|
|
266
|
+
|
|
265
267
|
# Cache folder configuration
|
|
266
268
|
action_folder_map = {
|
|
267
269
|
"pr": "pr_desc",
|
|
@@ -299,6 +301,15 @@ def generate_pr_content(action_folder, action_type, diff_text, provider="gemini"
|
|
|
299
301
|
cached_data = get_cached_response(action_folder, prompt)
|
|
300
302
|
if cached_data:
|
|
301
303
|
click.secho(__("⚡ Response retrieved from local cache."), fg="green", dim=True)
|
|
304
|
+
from src.metrics import log_command_metric
|
|
305
|
+
duration_ms = int((time.perf_counter() - t_start) * 1000)
|
|
306
|
+
log_command_metric(
|
|
307
|
+
command=action_type,
|
|
308
|
+
status="success",
|
|
309
|
+
provider=provider,
|
|
310
|
+
duration_ms=duration_ms,
|
|
311
|
+
cache_hit=True,
|
|
312
|
+
)
|
|
302
313
|
return cached_data
|
|
303
314
|
|
|
304
315
|
# Key Preparation (Now dynamic per Provider)
|
|
@@ -318,13 +329,14 @@ def generate_pr_content(action_folder, action_type, diff_text, provider="gemini"
|
|
|
318
329
|
click.secho(__("🤖 GitPR is analyzing your code using {provider} ({model})...\n", provider=provider.capitalize(), model=api_model), fg="cyan")
|
|
319
330
|
|
|
320
331
|
chunks = split_diff_into_chunks(diff_text, max_tokens=90000)
|
|
321
|
-
total_meta = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}
|
|
322
|
-
|
|
332
|
+
total_meta = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "duration_ms": 0}
|
|
333
|
+
|
|
323
334
|
def _aggregate_meta(new_meta):
|
|
324
335
|
if new_meta:
|
|
325
336
|
total_meta["prompt_tokens"] += new_meta.get("prompt_tokens", 0)
|
|
326
337
|
total_meta["completion_tokens"] += new_meta.get("completion_tokens", 0)
|
|
327
338
|
total_meta["total_tokens"] += new_meta.get("total_tokens", 0)
|
|
339
|
+
total_meta["duration_ms"] += new_meta.get("duration_ms", 0)
|
|
328
340
|
|
|
329
341
|
if len(chunks) == 1:
|
|
330
342
|
result_json = call_ai_model(provider, api_key, api_model, prompt, instrucao_sistema, action=action_folder)
|
|
@@ -375,18 +387,21 @@ def generate_pr_content(action_folder, action_type, diff_text, provider="gemini"
|
|
|
375
387
|
save_cached_response(action_folder, action_type, prompt, result_json, meta_raw=total_meta)
|
|
376
388
|
# Fire-and-forget metric for successful AI-powered command
|
|
377
389
|
from src.metrics import log_command_metric
|
|
390
|
+
duration_ms = int((time.perf_counter() - t_start) * 1000)
|
|
378
391
|
log_command_metric(
|
|
379
392
|
command=action_type,
|
|
380
393
|
status="success",
|
|
381
394
|
provider=provider,
|
|
382
395
|
tokens_estimated=total_meta.get("total_tokens", 0),
|
|
396
|
+
duration_ms=duration_ms,
|
|
383
397
|
map_reduce_triggered=(len(chunks) > 1),
|
|
384
398
|
)
|
|
385
399
|
return result_json
|
|
386
400
|
|
|
387
401
|
# Command failed (AI returned None)
|
|
388
402
|
from src.metrics import log_command_metric
|
|
389
|
-
|
|
403
|
+
duration_ms = int((time.perf_counter() - t_start) * 1000)
|
|
404
|
+
log_command_metric(command=action_type, status="error", provider=provider, duration_ms=duration_ms)
|
|
390
405
|
return None
|
|
391
406
|
|
|
392
407
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import subprocess
|
|
2
2
|
import re
|
|
3
3
|
import os
|
|
4
|
+
import time
|
|
4
5
|
import click
|
|
5
6
|
from src.ai_providers import call_ai_model
|
|
6
7
|
from src.cache import get_cached_response, save_cached_response
|
|
@@ -32,14 +33,20 @@ def get_github_repo_info():
|
|
|
32
33
|
|
|
33
34
|
def generate_issue_content(context_text, context_type="diff"):
|
|
34
35
|
"""Sends the context (diff, blame, or history) to the AI and returns an issue dictionary."""
|
|
36
|
+
from src.metrics import log_command_metric
|
|
37
|
+
|
|
35
38
|
if not context_text or not str(context_text).strip():
|
|
36
39
|
return None
|
|
37
40
|
|
|
41
|
+
t_start = time.perf_counter()
|
|
42
|
+
|
|
38
43
|
provider = get_ai_provider()
|
|
39
44
|
api_key = get_api_key(provider)
|
|
40
45
|
|
|
41
46
|
if not api_key:
|
|
42
47
|
click.secho(__("❌ Error: API Key not found."), fg="red")
|
|
48
|
+
duration_ms = int((time.perf_counter() - t_start) * 1000)
|
|
49
|
+
log_command_metric(command="issue", status="error", provider=provider, duration_ms=duration_ms)
|
|
43
50
|
return None
|
|
44
51
|
|
|
45
52
|
# Use the advanced model to ensure Issue structure quality
|
|
@@ -69,11 +76,13 @@ def generate_issue_content(context_text, context_type="diff"):
|
|
|
69
76
|
__("Generate the requested JSON object following the system instructions to {target_action}\n\n", target_action=target_action) +
|
|
70
77
|
f"{data_label}\n{context_text}"
|
|
71
78
|
)
|
|
72
|
-
|
|
79
|
+
|
|
73
80
|
# Try to retrieve from Cache
|
|
74
81
|
cached_data = get_cached_response("issue", prompt)
|
|
75
82
|
if cached_data:
|
|
76
83
|
click.secho(__("⚡ Issue response retrieved from local cache."), fg="green", dim=True)
|
|
84
|
+
duration_ms = int((time.perf_counter() - t_start) * 1000)
|
|
85
|
+
log_command_metric(command="issue", status="success", provider=provider, duration_ms=duration_ms, cache_hit=True)
|
|
77
86
|
return cached_data
|
|
78
87
|
|
|
79
88
|
click.secho(__("🤖 Structuring Issue using {provider} ({api_model})...", provider=provider.capitalize(), api_model=api_model), fg="cyan", dim=True)
|
|
@@ -81,8 +90,19 @@ def generate_issue_content(context_text, context_type="diff"):
|
|
|
81
90
|
result_json = call_ai_model(provider, api_key, api_model, prompt, sys_inst, action="issue")
|
|
82
91
|
|
|
83
92
|
if result_json and "titulo" in result_json and "corpo" in result_json:
|
|
84
|
-
#
|
|
85
|
-
|
|
93
|
+
# Extract telemetry metadata and save to cache with real token counts
|
|
94
|
+
meta = result_json.pop("_telemetry_meta", None)
|
|
95
|
+
save_cached_response("issue", "issue", prompt, result_json, meta_raw=meta)
|
|
96
|
+
duration_ms = int((time.perf_counter() - t_start) * 1000)
|
|
97
|
+
log_command_metric(
|
|
98
|
+
command="issue",
|
|
99
|
+
status="success",
|
|
100
|
+
provider=provider,
|
|
101
|
+
tokens_estimated=(meta or {}).get("total_tokens", 0),
|
|
102
|
+
duration_ms=duration_ms,
|
|
103
|
+
)
|
|
86
104
|
return result_json
|
|
87
|
-
|
|
105
|
+
|
|
106
|
+
duration_ms = int((time.perf_counter() - t_start) * 1000)
|
|
107
|
+
log_command_metric(command="issue", status="error", provider=provider, duration_ms=duration_ms)
|
|
88
108
|
return {"titulo": __("Error generating title"), "corpo": __("Could not generate issue body by AI.")}
|
|
@@ -250,7 +250,8 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, install
|
|
|
250
250
|
|
|
251
251
|
if metrics and export:
|
|
252
252
|
from src.metrics import export_metrics
|
|
253
|
-
|
|
253
|
+
from src.core import get_repo_name
|
|
254
|
+
csv_path, json_path, count = export_metrics(repo_filter=get_repo_name())
|
|
254
255
|
if count > 0:
|
|
255
256
|
click.secho(__("✅ Metrics exported: {count} events.", count=count), fg="green", bold=True)
|
|
256
257
|
if csv_path:
|
|
@@ -270,9 +271,10 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, install
|
|
|
270
271
|
click.secho(__("Purge cancelled."), fg="yellow")
|
|
271
272
|
return
|
|
272
273
|
|
|
273
|
-
if
|
|
274
|
+
if show_dashboard:
|
|
274
275
|
from src.ui.metrics_app import launch_metrics_dashboard
|
|
275
|
-
|
|
276
|
+
from src.core import get_repo_name
|
|
277
|
+
launch_metrics_dashboard(repo_filter=get_repo_name())
|
|
276
278
|
return
|
|
277
279
|
|
|
278
280
|
if metrics:
|
|
@@ -364,20 +366,27 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, install
|
|
|
364
366
|
if update:
|
|
365
367
|
click.secho(__("🔍 Checking for updates..."), fg="cyan")
|
|
366
368
|
check_and_update()
|
|
369
|
+
from src.metrics import log_command_metric
|
|
370
|
+
log_command_metric(command="update", status="success", provider="git")
|
|
367
371
|
return
|
|
368
372
|
|
|
369
373
|
# --install option: Interactive setup wizard
|
|
370
374
|
if install:
|
|
371
375
|
run_install_wizard()
|
|
376
|
+
from src.metrics import log_command_metric
|
|
377
|
+
log_command_metric(command="install", status="success", provider="git")
|
|
372
378
|
return
|
|
373
379
|
|
|
374
380
|
# --skill option: Generate template and exit
|
|
375
381
|
if skill:
|
|
376
382
|
generate_skill_template()
|
|
383
|
+
from src.metrics import log_command_metric
|
|
384
|
+
log_command_metric(command="skill", status="success", provider="git")
|
|
377
385
|
return
|
|
378
386
|
|
|
379
387
|
if installhooks:
|
|
380
|
-
|
|
388
|
+
hooks_ok = install_git_hooks()
|
|
389
|
+
if hooks_ok:
|
|
381
390
|
click.secho("\n" + __("✅ Git Hooks successfully installed!"), fg="green", bold=True)
|
|
382
391
|
click.echo(__("The Linter will now run automatically before each commit."))
|
|
383
392
|
|
|
@@ -392,6 +401,8 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, install
|
|
|
392
401
|
click.echo(__("• How to create new Linter rules (.gitpr.linter.yml):"))
|
|
393
402
|
click.secho(f" {get_doc_url("linter-regras-customizadas.md")}", fg="blue")
|
|
394
403
|
click.echo("---\n")
|
|
404
|
+
from src.metrics import log_command_metric
|
|
405
|
+
log_command_metric(command="installhooks", status="success" if hooks_ok else "error", provider="git")
|
|
395
406
|
return
|
|
396
407
|
|
|
397
408
|
# Archaeologist Module (--blame)
|
|
@@ -431,6 +442,8 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, install
|
|
|
431
442
|
# Trigger the engine
|
|
432
443
|
from src.blame_engine import run_blame_analysis
|
|
433
444
|
run_blame_analysis(file_path.strip(), start_line.strip(), end_line.strip())
|
|
445
|
+
from src.metrics import log_command_metric
|
|
446
|
+
log_command_metric(command="blame", status="success", provider="git")
|
|
434
447
|
return
|
|
435
448
|
|
|
436
449
|
# Issue Module (Hybrid)
|
|
@@ -526,19 +539,32 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, install
|
|
|
526
539
|
|
|
527
540
|
# Validate or request PAT Token
|
|
528
541
|
github_token = validate_or_request_github_token(repo_info)
|
|
529
|
-
|
|
542
|
+
|
|
530
543
|
if not github_token:
|
|
531
544
|
click.secho(__("❌ Access canceled. GitHub Token is mandatory for this action."), fg="red")
|
|
532
545
|
return
|
|
533
|
-
|
|
534
|
-
# Run the Terminal Graphical Interface
|
|
535
|
-
app = IssueApp(issue_data=issue_data, repo_info=repo_info, github_token=github_token)
|
|
536
|
-
app.run()
|
|
537
546
|
|
|
538
|
-
#
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
547
|
+
# Run the Terminal Graphical Interface (with reauth loop for expired tokens)
|
|
548
|
+
while True:
|
|
549
|
+
app = IssueApp(issue_data=issue_data, repo_info=repo_info, github_token=github_token)
|
|
550
|
+
app.run()
|
|
551
|
+
|
|
552
|
+
# If token expired during the TUI session, re-prompt and relaunch
|
|
553
|
+
if app.final_action == "reauth":
|
|
554
|
+
click.secho(f"\n{app.final_message}\n", fg="yellow")
|
|
555
|
+
github_token = validate_or_request_github_token(repo_info)
|
|
556
|
+
if not github_token:
|
|
557
|
+
click.secho(__("❌ Access canceled. GitHub Token is mandatory for this action."), fg="red")
|
|
558
|
+
return
|
|
559
|
+
# Restart TUI with the same issue data and new token
|
|
560
|
+
continue
|
|
561
|
+
|
|
562
|
+
# Display return message after closing the TUI
|
|
563
|
+
if app.final_message:
|
|
564
|
+
cor = "green" if app.final_action in ["saved", "created"] else "red"
|
|
565
|
+
click.secho(f"\n{app.final_message}\n", fg=cor, bold=True)
|
|
566
|
+
|
|
567
|
+
break
|
|
542
568
|
|
|
543
569
|
return
|
|
544
570
|
|
|
@@ -590,6 +616,8 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, install
|
|
|
590
616
|
system_instruction=system_instruction
|
|
591
617
|
)
|
|
592
618
|
app.run()
|
|
619
|
+
from src.metrics import log_command_metric
|
|
620
|
+
log_command_metric(command="chat", status="success", provider=active_provider)
|
|
593
621
|
click.secho(f"📚 {__('Chat documentation:')} {get_doc_url('understanding_chat_functionality.md')}", fg="cyan")
|
|
594
622
|
return
|
|
595
623
|
|