gitpr-cli 0.0.15__tar.gz → 0.0.16__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 (24) hide show
  1. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/PKG-INFO +5 -2
  2. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/README.md +4 -1
  3. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/gitpr_cli.egg-info/PKG-INFO +5 -2
  4. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/pyproject.toml +1 -1
  5. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/blame_engine.py +15 -7
  6. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/cache.py +34 -1
  7. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/core.py +46 -2
  8. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/issue_engine.py +19 -8
  9. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/main.py +71 -15
  10. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/tui_issue.py +1 -1
  11. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/updater.py +1 -1
  12. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/LICENSE +0 -0
  13. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/gitpr_cli.egg-info/SOURCES.txt +0 -0
  14. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/gitpr_cli.egg-info/dependency_links.txt +0 -0
  15. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/gitpr_cli.egg-info/entry_points.txt +0 -0
  16. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/gitpr_cli.egg-info/requires.txt +0 -0
  17. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/gitpr_cli.egg-info/top_level.txt +0 -0
  18. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/setup.cfg +0 -0
  19. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/__init__.py +0 -0
  20. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/ai_providers.py +0 -0
  21. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/config.py +0 -0
  22. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/linter_engine.py +0 -0
  23. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/src/security.py +0 -0
  24. {gitpr_cli-0.0.15 → gitpr_cli-0.0.16}/tests/test_core.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpr-cli
3
- Version: 0.0.15
3
+ Version: 0.0.16
4
4
  Summary: Automação de PRs, Commits e Code Review com IA (Gemini e DeepSeek)
5
5
  Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
6
6
  Requires-Python: >=3.10
@@ -126,7 +126,10 @@ Você pode passar as seguintes *flags* para ações específicas:
126
126
  * `-l` ou `--linter`: Roda **apenas o linter estático local** (sem chamadas de IA). Ideal para usar em pipelines de CI/CD para bloquear código fora do padrão.
127
127
  * `-ih` ou `--installhooks`: Instala automaticamente os **Git Hooks locais** (`pre-commit` e `prepare-commit-msg`) no seu repositório.
128
128
  * `-s` ou `--skill`: Cria os arquivos de template de contexto da IA (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`) e do Linter (`.gitpr.linter.yml`) na raiz do projeto.
129
- * `-is` ou `--issue`: Gera automaticamente o rascunho de uma **Issue padronizada** a partir do diff e abre uma interface interativa (TUI) para edição, salvamento ou envio direto para o repositório no GitHub via API REST.
129
+ * `-is` ou `--issue`: Gera automaticamente o rascunho de uma **Issue padronizada** e abre uma interface interativa (TUI) para edição ou envio direto via API REST. Esta funcionalidade possui **3 motores de contexto** dependendo da combinação de comandos:
130
+ * **Issue de Código Novo (`gitpr -is`):** Lê o `git diff` atual. **Por que usar:** Ideal para documentar rapidamente a tarefa que você acabou de programar, antes de commitar.
131
+ * **Issue de Épico/Release (`gitpr -is -ht`):** Lê o histórico completo da branch atual (Git Log + Cache de PRs). **Por que usar:** Ideal para gerar uma documentação consolidada de uma release inteira ou de uma *feature* grande que levou vários dias/commits para ser concluída.
132
+ * **Issue Arqueológica/Dívida Técnica (`gitpr -is -b arquivo:linhas`):** Lê a linha do tempo de uma regra específica. **Por que usar:** Ideal para documentar dívidas técnicas, explicando como um bloco de código legado evoluiu e por que precisa ser refatorado.
130
133
  * `-u` ou `--update`: Verifica e instala a versão mais recente do GitPR (Auto-Updater).
131
134
  * `-h` ou `--help`: Exibe o menu de ajuda.
132
135
 
@@ -110,7 +110,10 @@ Você pode passar as seguintes *flags* para ações específicas:
110
110
  * `-l` ou `--linter`: Roda **apenas o linter estático local** (sem chamadas de IA). Ideal para usar em pipelines de CI/CD para bloquear código fora do padrão.
111
111
  * `-ih` ou `--installhooks`: Instala automaticamente os **Git Hooks locais** (`pre-commit` e `prepare-commit-msg`) no seu repositório.
112
112
  * `-s` ou `--skill`: Cria os arquivos de template de contexto da IA (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`) e do Linter (`.gitpr.linter.yml`) na raiz do projeto.
113
- * `-is` ou `--issue`: Gera automaticamente o rascunho de uma **Issue padronizada** a partir do diff e abre uma interface interativa (TUI) para edição, salvamento ou envio direto para o repositório no GitHub via API REST.
113
+ * `-is` ou `--issue`: Gera automaticamente o rascunho de uma **Issue padronizada** e abre uma interface interativa (TUI) para edição ou envio direto via API REST. Esta funcionalidade possui **3 motores de contexto** dependendo da combinação de comandos:
114
+ * **Issue de Código Novo (`gitpr -is`):** Lê o `git diff` atual. **Por que usar:** Ideal para documentar rapidamente a tarefa que você acabou de programar, antes de commitar.
115
+ * **Issue de Épico/Release (`gitpr -is -ht`):** Lê o histórico completo da branch atual (Git Log + Cache de PRs). **Por que usar:** Ideal para gerar uma documentação consolidada de uma release inteira ou de uma *feature* grande que levou vários dias/commits para ser concluída.
116
+ * **Issue Arqueológica/Dívida Técnica (`gitpr -is -b arquivo:linhas`):** Lê a linha do tempo de uma regra específica. **Por que usar:** Ideal para documentar dívidas técnicas, explicando como um bloco de código legado evoluiu e por que precisa ser refatorado.
114
117
  * `-u` ou `--update`: Verifica e instala a versão mais recente do GitPR (Auto-Updater).
115
118
  * `-h` ou `--help`: Exibe o menu de ajuda.
116
119
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpr-cli
3
- Version: 0.0.15
3
+ Version: 0.0.16
4
4
  Summary: Automação de PRs, Commits e Code Review com IA (Gemini e DeepSeek)
5
5
  Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
6
6
  Requires-Python: >=3.10
@@ -126,7 +126,10 @@ Você pode passar as seguintes *flags* para ações específicas:
126
126
  * `-l` ou `--linter`: Roda **apenas o linter estático local** (sem chamadas de IA). Ideal para usar em pipelines de CI/CD para bloquear código fora do padrão.
127
127
  * `-ih` ou `--installhooks`: Instala automaticamente os **Git Hooks locais** (`pre-commit` e `prepare-commit-msg`) no seu repositório.
128
128
  * `-s` ou `--skill`: Cria os arquivos de template de contexto da IA (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`) e do Linter (`.gitpr.linter.yml`) na raiz do projeto.
129
- * `-is` ou `--issue`: Gera automaticamente o rascunho de uma **Issue padronizada** a partir do diff e abre uma interface interativa (TUI) para edição, salvamento ou envio direto para o repositório no GitHub via API REST.
129
+ * `-is` ou `--issue`: Gera automaticamente o rascunho de uma **Issue padronizada** e abre uma interface interativa (TUI) para edição ou envio direto via API REST. Esta funcionalidade possui **3 motores de contexto** dependendo da combinação de comandos:
130
+ * **Issue de Código Novo (`gitpr -is`):** Lê o `git diff` atual. **Por que usar:** Ideal para documentar rapidamente a tarefa que você acabou de programar, antes de commitar.
131
+ * **Issue de Épico/Release (`gitpr -is -ht`):** Lê o histórico completo da branch atual (Git Log + Cache de PRs). **Por que usar:** Ideal para gerar uma documentação consolidada de uma release inteira ou de uma *feature* grande que levou vários dias/commits para ser concluída.
132
+ * **Issue Arqueológica/Dívida Técnica (`gitpr -is -b arquivo:linhas`):** Lê a linha do tempo de uma regra específica. **Por que usar:** Ideal para documentar dívidas técnicas, explicando como um bloco de código legado evoluiu e por que precisa ser refatorado.
130
133
  * `-u` ou `--update`: Verifica e instala a versão mais recente do GitPR (Auto-Updater).
131
134
  * `-h` ou `--help`: Exibe o menu de ajuda.
132
135
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gitpr-cli"
7
- version = "0.0.15"
7
+ version = "0.0.16"
8
8
  description = "Automação de PRs, Commits e Code Review com IA (Gemini e DeepSeek)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -89,19 +89,23 @@ def analyze_commit_with_ai(commit_hash, file_path):
89
89
 
90
90
  return {"status": "ORIGEM", "motivo": "IA não retornou formato válido."}
91
91
 
92
- def run_blame_analysis(file_path, start_line, end_line):
92
+ def run_blame_analysis(file_path, start_line, end_line, return_data=False):
93
93
  """Motor de Loop Temporal que constrói a Timeline consolidada."""
94
- click.secho(f"\n🔍 Iniciando Arqueologia de Código...", fg="cyan", bold=True)
95
- click.echo(f"📍 Arquivo: {file_path} (Linhas: {start_line} até {end_line})")
94
+
95
+ # Se for acionado para retorno de dados (via --issue), silencia os prints
96
+ if not return_data:
97
+ click.secho(f"\n🔍 Iniciando Arqueologia de Código...", fg="cyan", bold=True)
98
+ click.echo(f"📍 Arquivo: {file_path} (Linhas: {start_line} até {end_line})")
96
99
 
97
100
  initial_commits = execute_git_blame(file_path, start_line, end_line)
98
101
 
99
102
  if not initial_commits:
100
- click.secho("⚠️ Nenhum commit rastreável encontrado nestas linhas.", fg="yellow")
101
- return
103
+ if not return_data:
104
+ click.secho("⚠️ Nenhum commit rastreável encontrado nestas linhas.", fg="yellow")
105
+ return [] if return_data else None
102
106
 
103
- click.secho(f"✅ Encontrado(s) {len(initial_commits)} commit(s) na superfície. Iniciando viagem no tempo...\n", fg="green")
104
-
107
+ if not return_data:
108
+ click.secho(f"✅ Encontrado(s) {len(initial_commits)} commit(s) na superfície. Iniciando viagem no tempo...\n", fg="green")
105
109
  master_timeline = []
106
110
  seen_hashes = set()
107
111
 
@@ -146,6 +150,10 @@ def run_blame_analysis(file_path, start_line, end_line):
146
150
  # ORDENAÇÃO CRONOLÓGICA (Do mais antigo para o mais novo)
147
151
  master_timeline.sort(key=lambda x: x["raw_date"])
148
152
 
153
+ # Retorno Direto para a IA
154
+ if return_data:
155
+ return master_timeline
156
+
149
157
  # EXIBIÇÃO VISUAL NO TERMINAL (ÚNICA)
150
158
  click.secho(f"\n📜 Histórico Consolidado da Regra (Linhas {start_line}-{end_line}):", fg="magenta", bold=True)
151
159
 
@@ -3,6 +3,7 @@ import json
3
3
  import hashlib
4
4
  from datetime import datetime
5
5
  from pathlib import Path
6
+ from src.core import get_current_branch
6
7
 
7
8
  def get_cache_base_dir():
8
9
  """Retorna o caminho ~/.gitpr/cache/prompts/"""
@@ -34,9 +35,11 @@ def save_cached_response(action_folder, action_type, prompt_text, response_dict)
34
35
  folder_path.mkdir(parents=True, exist_ok=True)
35
36
 
36
37
  cache_file = folder_path / f"{md5_hash}.json"
38
+ current_branch = get_current_branch()
37
39
 
38
40
  cache_data = {
39
41
  "md5": md5_hash,
42
+ "branch": current_branch,
40
43
  "datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
41
44
  "action_type": action_type,
42
45
  "prompt": prompt_text,
@@ -47,4 +50,34 @@ def save_cached_response(action_folder, action_type, prompt_text, response_dict)
47
50
  with open(cache_file, "w", encoding="utf-8") as f:
48
51
  json.dump(cache_data, f, indent=2, ensure_ascii=False)
49
52
  except IOError:
50
- pass # Falha silenciosa no cache para não travar a ferramenta
53
+ pass # Falha silenciosa no cache para não travar a ferramenta
54
+
55
+ def get_cached_pr_descriptions(branch_name):
56
+ """Busca no cache todos os PRs gerados historicamente para esta branch."""
57
+ pr_cache_folder = get_cache_base_dir() / "pr_desc"
58
+ history_texts = []
59
+
60
+ if not pr_cache_folder.exists():
61
+ return ""
62
+
63
+ for cache_file in pr_cache_folder.glob("*.json"):
64
+ try:
65
+ with open(cache_file, "r", encoding="utf-8") as f:
66
+ data = json.load(f)
67
+
68
+ # Verifica se o cache pertence à branch solicitada
69
+ if data.get("branch") == branch_name:
70
+ response_dict = data.get("response", {})
71
+ pr_desc = response_dict.get("pr_description")
72
+ if pr_desc:
73
+ date_str = data.get("datetime", "Data desconhecida")
74
+ history_texts.append(f"[{date_str}]\n{pr_desc}\n")
75
+ except (json.JSONDecodeError, IOError):
76
+ continue
77
+
78
+ if history_texts:
79
+ # Ordena cronologicamente (usando a data extraída no colchete)
80
+ history_texts.sort()
81
+ return "=== HISTÓRICO DE PRs DA IA ===\n" + "\n".join(history_texts)
82
+
83
+ return ""
@@ -7,11 +7,12 @@ import urllib.request
7
7
  import urllib.error
8
8
  from google import genai
9
9
  from src.security import decrypt_data
10
- from src.cache import get_cached_response, save_cached_response
10
+ from src.cache import get_cached_response, save_cached_response, get_cached_pr_descriptions
11
11
  from src.config import get_api_key, get_api_model
12
12
  from src.ai_providers import call_ai_model
13
13
 
14
14
 
15
+
15
16
  def get_git_diff():
16
17
  """Executa 'git diff HEAD' e retorna a saída, alertando sobre arquivos não monitorados (untracked)."""
17
18
  try:
@@ -312,4 +313,47 @@ def install_git_hooks():
312
313
  except Exception as e:
313
314
  click.secho(f"⚠️ Falha ao instalar {hook_name}: {e}", fg="yellow")
314
315
 
315
- return success_count == len(hooks_to_install)
316
+ return success_count == len(hooks_to_install)
317
+
318
+ def get_branch_history_text():
319
+ """Compila o Git Log e o Cache de PRs da branch atual para gerar o contexto épico."""
320
+ branch = get_current_branch()
321
+ base_branch = get_base_branch()
322
+
323
+ click.secho(f"🔄 Compilando histórico da branch '{branch}' contra '{base_branch}'...", fg="cyan")
324
+
325
+ hybrid_context = f"Resumo Histórico da Branch: {branch}\n\n"
326
+
327
+ # Pega os Commits reais do Git
328
+ try:
329
+ # Pega a linha do tempo desde o merge base
330
+ merge_base_res = subprocess.run(
331
+ ["git", "merge-base", f"origin/{base_branch}", "HEAD"],
332
+ capture_output=True, text=True, check=True
333
+ )
334
+ ancestor_hash = merge_base_res.stdout.strip()
335
+
336
+ # Formato: Hash | Data | Autor | Mensagem
337
+ git_log_res = subprocess.run(
338
+ ["git", "log", f"{ancestor_hash}..HEAD", "--format=%h | %ad | %an | %s", "--date=short"],
339
+ capture_output=True, text=True, encoding="utf-8", check=True
340
+ )
341
+ git_log = git_log_res.stdout.strip()
342
+
343
+ hybrid_context += "=== COMMITS REGISTRADOS ===\n"
344
+ if git_log:
345
+ hybrid_context += f"{git_log}\n\n"
346
+ else:
347
+ hybrid_context += "Nenhum commit exclusivo encontrado nesta branch.\n\n"
348
+
349
+ except subprocess.CalledProcessError as e:
350
+ click.secho(f"⚠️ Aviso: Não foi possível obter o Git Log: {e.stderr}", fg="yellow")
351
+
352
+ # Pega a memória histórica da IA (Cache de PRs antigos dessa branch)
353
+ cached_prs = get_cached_pr_descriptions(branch)
354
+ if cached_prs:
355
+ hybrid_context += f"{cached_prs}\n"
356
+ else:
357
+ hybrid_context += "=== HISTÓRICO DE PRs DA IA ===\nNenhum PR anterior gerado por IA encontrado em cache para esta branch.\n"
358
+
359
+ return hybrid_context
@@ -29,9 +29,9 @@ def get_github_repo_info():
29
29
  except subprocess.CalledProcessError:
30
30
  return None
31
31
 
32
- def generate_issue_content(diff_text):
33
- """Envia o diff para a IA e retorna um dicionário com título e corpo da issue."""
34
- if not diff_text or not diff_text.strip():
32
+ def generate_issue_content(context_text, context_type="diff"):
33
+ """Envia o contexto (diff, blame ou history) para a IA e retorna um dicionário da issue."""
34
+ if not context_text or not str(context_text).strip():
35
35
  return None
36
36
 
37
37
  provider = get_ai_provider()
@@ -53,12 +53,23 @@ def generate_issue_content(diff_text):
53
53
  else:
54
54
  sys_inst = "Você é um Arquiteto de Software. Siga o formato O Que / Por Que / Onde / Como para documentar a Issue."
55
55
 
56
+ # Cérebro Adaptativo (Prompt Dinâmico)
57
+ if context_type == "blame":
58
+ target_action = "documentar a evolução arquitetural, refatorações e a dívida técnica desta regra de negócio baseando-se no histórico de commits."
59
+ data_label = "LINHA DO TEMPO DA REGRA (DO MAIS ANTIGO PARA O MAIS NOVO):"
60
+ elif context_type == "history":
61
+ target_action = "documentar o Épico/Release detalhando todas as funcionalidades implementadas baseando-se no histórico integral da branch."
62
+ data_label = "HISTÓRICO CONSOLIDADO DA BRANCH (COMMITS + PRS ANTIGOS):"
63
+ else:
64
+ target_action = "documentar a seguinte alteração de código recém introduzida."
65
+ data_label = "DIFF PARA ANÁLISE:"
66
+
56
67
  prompt = (
57
- f"Gere o objeto JSON solicitado seguindo as instruções de sistema para documentar a seguinte alteração:\n\n"
58
- f"DIFF PARA ANÁLISE:\n{diff_text}"
68
+ f"Gere o objeto JSON solicitado seguindo as instruções de sistema para {target_action}\n\n"
69
+ f"{data_label}\n{context_text}"
59
70
  )
60
-
61
- # --- NOVO: Tenta recuperar do Cache ---
71
+
72
+ # Tenta recuperar do Cache
62
73
  cached_data = get_cached_response("issue", prompt)
63
74
  if cached_data:
64
75
  click.secho("⚡ Resposta da Issue recuperada do cache local.", fg="green", dim=True)
@@ -69,7 +80,7 @@ def generate_issue_content(diff_text):
69
80
  result_json = call_ai_model(provider, api_key, api_model, prompt, sys_inst)
70
81
 
71
82
  if result_json and "titulo" in result_json and "corpo" in result_json:
72
- # --- NOVO: Salva no Cache ---
83
+ # Salva no Cache
73
84
  save_cached_response("issue", "issue", prompt, result_json)
74
85
  return result_json
75
86
 
@@ -12,7 +12,8 @@ from src.core import (
12
12
  get_current_branch,
13
13
  generate_pr_content,
14
14
  generate_skill_template,
15
- install_git_hooks
15
+ install_git_hooks,
16
+ get_branch_history_text
16
17
  )
17
18
  from src.linter_engine import parse_diff_and_lint
18
19
 
@@ -46,9 +47,10 @@ def print_banner():
46
47
  @click.option('-q', '--quiet', is_flag=True, hidden=True, help="Oculta o banner e logs não essenciais (uso interno).")
47
48
  @click.option('-i', '--input', type=click.Path(exists=True), help="Caminho de um arquivo específico para análise completa.")
48
49
  @click.option('-b', '--blame', type=str, help="Analisa a origem de uma regra de negócio (ex: arquivo.py:10-20 ou apenas arquivo.py).")
50
+ @click.option('-ht', '--history', is_flag=True, help="Usa todo o histórico da branch (Git Log + Cache de PR) como contexto para gerar a issue.")
49
51
  @click.option('-is', '--issue', is_flag=True, help="Gera uma Issue padronizada das alterações atuais e abre a interface interativa.")
50
52
  @click.option('-p', '--provider', type=click.Choice(['gemini', 'deepseek']), help="Força a utilização de um provedor de IA específico nesta execução.")
51
- def cli(commit, review, fullreview, linter, skill, update, installhooks, hook, quiet, provider, input, blame, issue):
53
+ def cli(commit, review, fullreview, linter, skill, update, installhooks, hook, quiet, provider, input, blame,history, issue):
52
54
  """
53
55
  GitPR CLI - Automação de PRs e Code Review com IA.
54
56
 
@@ -140,7 +142,7 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, hook, q
140
142
  return
141
143
 
142
144
  # Módulo Arqueólogo (--blame)
143
- if blame:
145
+ if blame and not issue:
144
146
  # Parser para separar o arquivo das linhas
145
147
  if ":" in blame:
146
148
  # Modo Direto: gitpr --blame arquivo:10-20
@@ -177,23 +179,77 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, hook, q
177
179
  from src.blame_engine import run_blame_analysis
178
180
  run_blame_analysis(file_path.strip(), start_line.strip(), end_line.strip())
179
181
  return
180
-
181
- # Módulo Issue (--issue)
182
+
183
+ # Módulo Issue (Híbrido)
182
184
  if issue:
183
- # Importações sob demanda para não atrasar a CLI se não for usar a TUI
184
185
  from src.issue_engine import generate_issue_content, get_github_repo_info
185
186
  from src.tui_issue import validate_or_request_github_token, IssueApp
186
187
 
187
- diff_text = get_git_diff()
188
- if not diff_text or not diff_text.strip():
189
- click.secho("\n⚠️ Nenhum código novo encontrado. Faça alguma alteração antes de gerar a issue.\n", fg="yellow")
190
- return
191
-
192
- # Garante que as chaves da IA estão configuradas antes de chamar o motor
193
188
  setup_environment()
194
189
 
195
- # Gera o conteúdo com a IA
196
- issue_data = generate_issue_content(diff_text)
190
+ context_text = ""
191
+ context_type = "diff"
192
+
193
+ # Roteador de Contexto Inteligente
194
+ if history:
195
+ context_type = "history"
196
+ context_text = get_branch_history_text()
197
+ if not context_text or ("Nenhum commit exclusivo" in context_text and "Nenhum PR anterior" in context_text):
198
+ click.secho("\n⚠️ Nenhum histórico encontrado para esta branch.\n", fg="yellow")
199
+ return
200
+
201
+ elif blame:
202
+ context_type = "blame"
203
+ # Reaproveita o parser de arquivo/linhas da opção blame
204
+ if ":" in blame:
205
+ file_path, lines = blame.split(":", 1)
206
+ try:
207
+ start_line, end_line = lines.split("-") if "-" in lines else (lines, lines)
208
+ except ValueError:
209
+ click.secho("❌ Formato de linhas inválido. Use inicio-fim (ex: 10-20).", fg="red")
210
+ return
211
+ else:
212
+ file_path = blame
213
+ if not os.path.exists(file_path):
214
+ click.secho(f"❌ O arquivo '{file_path}' não foi encontrado.", fg="red")
215
+ return
216
+ click.secho(f"📂 Arquivo selecionado: {file_path}", fg="cyan", bold=True)
217
+ lines_input = click.prompt("Quais linhas você deseja investigar? (Ex: 10-20)")
218
+ start_line, end_line = lines_input.split("-") if "-" in lines_input else (lines_input, lines_input)
219
+
220
+ if not os.path.exists(file_path.strip()):
221
+ click.secho(f"❌ O arquivo '{file_path}' não foi encontrado.", fg="red")
222
+ return
223
+
224
+ from src.blame_engine import run_blame_analysis
225
+ click.secho("🔍 Extraindo linha do tempo arqueológica em background...", fg="cyan")
226
+
227
+ # Aciona o arqueólogo no modo silencioso
228
+ timeline = run_blame_analysis(file_path.strip(), start_line.strip(), end_line.strip(), return_data=True)
229
+
230
+ if not timeline:
231
+ click.secho("\n⚠️ Nenhum histórico rastreável para alimentar a issue.\n", fg="yellow")
232
+ return
233
+
234
+ # Traduz a lista de dicionários para um texto legível para a IA
235
+ formatted_timeline = []
236
+ for item in timeline:
237
+ formatted_timeline.append(
238
+ f"[{item['raw_date']}] Commit {item['hash']} por {item['info']['author']}:\n"
239
+ f"Ação: {item['status']}\n"
240
+ f"Motivo IA: {item['motivo']}\n"
241
+ )
242
+ context_text = "\n".join(formatted_timeline)
243
+
244
+ else:
245
+ context_type = "diff"
246
+ context_text = get_git_diff()
247
+ if not context_text or not context_text.strip():
248
+ click.secho("\n⚠️ Nenhum código novo encontrado. Faça alguma alteração antes de gerar a issue.\n", fg="yellow")
249
+ return
250
+
251
+ # Gera o conteúdo com a IA enviando o idioma correto
252
+ issue_data = generate_issue_content(context_text, context_type=context_type)
197
253
  if not issue_data:
198
254
  return
199
255
 
@@ -217,7 +273,7 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, hook, q
217
273
  click.secho(f"\n{app.final_message}\n", fg=cor, bold=True)
218
274
 
219
275
  return
220
-
276
+
221
277
  # Validação do Modo Input
222
278
  if input and not (review or fullreview):
223
279
  click.secho("\n❌ Erro: A opção --input (-i) só pode ser utilizada em conjunto com --review (-r) ou --fullreview (-f).", fg="red", bold=True)
@@ -20,7 +20,7 @@ def validate_or_request_github_token(repo_info):
20
20
  url_token = f"https://github.com/settings/tokens/new?scopes=repo&description=GitPR+Token+({repo_param})"
21
21
  click.secho(f"👉 {url_token}\n", fg="blue", underline=True)
22
22
 
23
- # --- NOVO: Link dinâmico para a documentação técnica ---
23
+ # Link dinâmico para a documentação técnica
24
24
  click.secho("📚 Entenda por que precisamos do Token e como ele é protegido por criptografia:", fg="cyan", dim=True)
25
25
  click.secho("👉 https://github.com/natanfiuza/gitpr/blob/main/docs/github-pat-integration.md\n", fg="blue", underline=True)
26
26
 
@@ -7,7 +7,7 @@ import click
7
7
  from datetime import datetime
8
8
 
9
9
  # Versão atual do seu executável local (Atualize isso a cada novo build!)
10
- __version__ = "0.0.15"
10
+ __version__ = "0.0.16"
11
11
  GITHUB_API_URL = "https://api.github.com/repos/natanfiuza/gitpr/releases/latest"
12
12
  PYPI_API_URL = "https://pypi.org/pypi/gitpr-cli/json"
13
13
 
File without changes
File without changes
File without changes
File without changes
File without changes