gemini-cli-pro 0.0.4-snapshot → 0.0.7-snapshot

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.
package/README.md CHANGED
@@ -52,5 +52,5 @@ Comandos do chat:
52
52
 
53
53
  O arquivo `~/.cache/gemini-history-chats/config.json` controla os MCPs (`local_rag`, `github`, `memory`).
54
54
  Se algum MCP nao estiver disponivel, o CLI continua funcionando no modo API.
55
- O MCP local de RAG usa `own-rag-cli` como nativo e cai para `own-rag` se existir.
55
+ O MCP local de RAG usa `rag-codebase` via comando `mcp-rag-server` quando estiver instalado.
56
56
  Em toda nova sessao, os MCPs sao recarregados automaticamente no startup.
package/bin/gemini CHANGED
@@ -11,6 +11,18 @@ done
11
11
  SCRIPT_DIR="$(cd -P "$(dirname "${SOURCE}")" && pwd)"
12
12
  PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
13
13
  PYTHON_BIN="${PYTHON_BIN:-python3}"
14
+ TOOL_PATH="${PROJECT_DIR}/gemini_tool.py"
15
+
16
+ if [ ! -f "${TOOL_PATH}" ] && command -v npm >/dev/null 2>&1; then
17
+ NPM_ROOT="$(npm root -g 2>/dev/null || true)"
18
+ for PKG_NAME in "gemini-cli-pro" "gemini-cli" "@jocsapb/gemini-cli"; do
19
+ CANDIDATE="${NPM_ROOT}/${PKG_NAME}/gemini_tool.py"
20
+ if [ -f "${CANDIDATE}" ]; then
21
+ TOOL_PATH="${CANDIDATE}"
22
+ break
23
+ fi
24
+ done
25
+ fi
14
26
 
15
27
  if ! command -v "${PYTHON_BIN}" >/dev/null 2>&1; then
16
28
  echo "Erro: python3 nao encontrado no PATH." >&2
@@ -23,4 +35,10 @@ if ! "${PYTHON_BIN}" -c "import google.generativeai" >/dev/null 2>&1; then
23
35
  exit 1
24
36
  fi
25
37
 
26
- exec "${PYTHON_BIN}" "${PROJECT_DIR}/gemini_tool.py" "$@"
38
+ if [ ! -f "${TOOL_PATH}" ]; then
39
+ echo "Erro: gemini_tool.py nao encontrado (procurei em ${TOOL_PATH})." >&2
40
+ echo "Reinstale com: npm install -g ." >&2
41
+ exit 1
42
+ fi
43
+
44
+ exec "${PYTHON_BIN}" "${TOOL_PATH}" "$@"
package/gemini_tool.py CHANGED
@@ -21,8 +21,21 @@ MEMORY_PATH = CACHE_DIR / "memory_store.json"
21
21
 
22
22
  MODEL_FLASH = "gemini-2.5-flash"
23
23
  MODEL_PRO = "gemini-2.5-pro"
24
- RAG_NATIVE_COMMAND = "own-rag-cli"
25
- RAG_FALLBACK_COMMAND = "own-rag"
24
+ RAG_SERVER_COMMAND = "mcp-rag-server"
25
+
26
+ ANSI_RESET = "\033[0m"
27
+ ANSI_BOLD = "\033[1m"
28
+ ANSI_DIM = "\033[2m"
29
+ ANSI_CYAN = "\033[96m"
30
+ ANSI_BLUE = "\033[94m"
31
+ ANSI_GREEN = "\033[92m"
32
+ ANSI_YELLOW = "\033[93m"
33
+ ANSI_RED = "\033[91m"
34
+ ANSI_MAGENTA = "\033[95m"
35
+
36
+
37
+ def colorize(text: str, color: str) -> str:
38
+ return f"{color}{text}{ANSI_RESET}"
26
39
 
27
40
 
28
41
  def ensure_cache_layout() -> None:
@@ -34,9 +47,9 @@ def ensure_cache_layout() -> None:
34
47
  "mcps": {
35
48
  "local_rag": {
36
49
  "enabled": True,
37
- "transport": "cli",
38
- "command": RAG_NATIVE_COMMAND,
39
- "description": "Local RAG via own-rag-cli (fallback own-rag)",
50
+ "transport": "mcp-stdio",
51
+ "server_command": RAG_SERVER_COMMAND,
52
+ "description": "Local RAG via servidor MCP rag-codebase",
40
53
  },
41
54
  "github": {
42
55
  "enabled": True,
@@ -85,7 +98,7 @@ def merge_dict(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]
85
98
  def require_api_key() -> str:
86
99
  api_key = os.getenv("GOOGLE_API_KEY")
87
100
  if not api_key:
88
- print("Erro: defina GOOGLE_API_KEY antes de iniciar.", file=sys.stderr)
101
+ print(colorize("Erro: defina GOOGLE_API_KEY antes de iniciar.", ANSI_RED), file=sys.stderr)
89
102
  sys.exit(1)
90
103
  return api_key
91
104
 
@@ -200,67 +213,103 @@ def emit_plin() -> None:
200
213
  print("\a", end="", flush=True)
201
214
 
202
215
 
203
- def _config_rag_candidates(config: dict[str, Any]) -> list[str]:
204
- candidates: list[str] = [RAG_NATIVE_COMMAND]
216
+ def _config_rag_server_candidates(config: dict[str, Any]) -> list[str]:
217
+ candidates: list[str] = [RAG_SERVER_COMMAND]
205
218
  mcps = config.get("mcps", {})
206
219
  if isinstance(mcps, dict):
207
220
  local_rag = mcps.get("local_rag", {})
208
221
  if isinstance(local_rag, dict):
209
- configured = local_rag.get("command")
222
+ configured = local_rag.get("server_command")
210
223
  if isinstance(configured, str) and configured.strip():
211
- candidates.append(configured.strip())
224
+ candidates.insert(0, configured.strip())
212
225
  if isinstance(configured, list):
213
226
  for item in configured:
214
227
  if isinstance(item, str) and item.strip():
215
- candidates.append(item.strip())
216
- candidates.append(RAG_FALLBACK_COMMAND)
217
- # remove duplicados preservando ordem
228
+ candidates.insert(0, item.strip())
218
229
  return list(dict.fromkeys(candidates))
219
230
 
220
231
 
221
- def resolve_rag_command(config: dict[str, Any] | None = None) -> str | None:
232
+ def resolve_rag_server_command(config: dict[str, Any] | None = None) -> str | None:
222
233
  effective_config = config if isinstance(config, dict) else load_config()
223
- for candidate in _config_rag_candidates(effective_config):
234
+ for candidate in _config_rag_server_candidates(effective_config):
224
235
  if shutil.which(candidate):
225
236
  return candidate
226
237
  return None
227
238
 
228
239
 
229
- def check_own_rag_installed(config: dict[str, Any] | None = None) -> bool:
230
- return resolve_rag_command(config) is not None
240
+ def check_rag_server_available(config: dict[str, Any] | None = None) -> bool:
241
+ return resolve_rag_server_command(config) is not None
231
242
 
232
243
 
233
244
  def rag_sync_current_dir(config: dict[str, Any] | None = None) -> tuple[bool, str]:
234
- rag_command = resolve_rag_command(config)
235
- if not rag_command:
236
- return False, "MISSING"
245
+ if resolve_rag_server_command(config):
246
+ # Com MCP rag-codebase disponível, considera backend RAG pronto.
247
+ return True, "OK"
248
+ return False, "OFF"
249
+
250
+
251
+ def _python_for_script(script_path: str) -> str:
252
+ try:
253
+ with open(script_path, "r", encoding="utf-8") as handle:
254
+ first_line = handle.readline().strip()
255
+ if first_line.startswith("#!") and "python" in first_line:
256
+ return first_line[2:].strip()
257
+ except Exception:
258
+ pass
259
+ return sys.executable
260
+
261
+
262
+ def search_local_context_via_rag_server(query: str) -> str:
263
+ server_command = resolve_rag_server_command()
264
+ if not server_command:
265
+ return ""
266
+
267
+ server_path = shutil.which(server_command)
268
+ if not server_path:
269
+ return ""
270
+
271
+ runner_code = r'''
272
+ import importlib.util
273
+ import pathlib
274
+ import sys
275
+
276
+ server_file = pathlib.Path(sys.argv[1]).expanduser().resolve()
277
+ query = sys.argv[2]
278
+ top_k = int(sys.argv[3])
279
+ mode = sys.argv[4]
280
+
281
+ spec = importlib.util.spec_from_file_location("mcp_rag_server_runtime", server_file)
282
+ if spec is None or spec.loader is None:
283
+ raise RuntimeError("falha ao carregar modulo do mcp-rag-server")
284
+ mod = importlib.util.module_from_spec(spec)
285
+ spec.loader.exec_module(mod)
237
286
 
238
- result = _run_command([rag_command, "sync", "."])
287
+ if not hasattr(mod, "semantic_search_code"):
288
+ raise RuntimeError("semantic_search_code nao encontrado no mcp-rag-server")
289
+
290
+ result = mod.semantic_search_code(query=query, top_k=top_k, mode=mode)
291
+ print(result if isinstance(result, str) else str(result))
292
+ '''
293
+
294
+ py_exec = _python_for_script(server_path)
295
+ result = _run_command([py_exec, "-c", runner_code, server_path, query, "6", "single"], timeout=180)
296
+ stdout = (result.stdout or "").strip()
297
+ stderr = (result.stderr or "").strip()
239
298
  if result.returncode == 0:
240
- return True, "OK"
241
- return False, "FAIL"
299
+ return stdout or "Nenhum resultado no rag-codebase."
300
+ return f"Erro no rag-codebase (code={result.returncode}): {stderr or stdout or 'sem detalhes'}"
242
301
 
243
302
 
244
303
  def search_local_context(query: str) -> str:
245
- """Busca no contexto local usando own-rag search."""
246
- rag_command = resolve_rag_command()
247
- if not rag_command:
248
- return "own-rag-cli/own-rag nao encontrado no PATH."
304
+ """Busca no contexto local usando o servidor MCP rag-codebase."""
305
+ server_command = resolve_rag_server_command()
306
+ if not server_command:
307
+ return ""
249
308
 
250
309
  query = (query or "").strip()
251
310
  if not query:
252
311
  return "Consulta vazia."
253
-
254
- result = _run_command([rag_command, "search", query])
255
- stdout = (result.stdout or "").strip()
256
- stderr = (result.stderr or "").strip()
257
-
258
- if result.returncode == 0:
259
- return stdout or "Nenhum resultado no RAG local."
260
- return (
261
- f"Erro no {rag_command} search (code={result.returncode}): "
262
- f"{stderr or stdout or 'sem detalhes'}"
263
- )
312
+ return search_local_context_via_rag_server(query)
264
313
 
265
314
 
266
315
  def github_tool(action: str, repo: str, path: str = "") -> str:
@@ -356,7 +405,7 @@ def _mcp_enabled(config: dict[str, Any], mcp_name: str, default: bool = True) ->
356
405
 
357
406
  def build_tools(config: dict[str, Any]) -> list[Any]:
358
407
  tools: list[Any] = []
359
- if _mcp_enabled(config, "local_rag", default=True) and check_own_rag_installed(config):
408
+ if _mcp_enabled(config, "local_rag", default=True) and check_rag_server_available(config):
360
409
  tools.append(as_executable(search_local_context))
361
410
  if _mcp_enabled(config, "github", default=True):
362
411
  tools.append(as_executable(github_tool))
@@ -383,6 +432,7 @@ class GeminiToolCLI:
383
432
  self.tools_active = False
384
433
  self.tools_error = ""
385
434
  self.rag_command: str | None = None
435
+ self.rag_server_found = False
386
436
  self.rag_ok = False
387
437
  self.rag_status = "MISSING"
388
438
 
@@ -391,6 +441,18 @@ class GeminiToolCLI:
391
441
  self.model = self._build_model(self.current_model_name)
392
442
  self.chat = self.model.start_chat(history=self.history)
393
443
 
444
+ def _print_info(self, message: str) -> None:
445
+ print(colorize(message, ANSI_BLUE))
446
+
447
+ def _print_success(self, message: str) -> None:
448
+ print(colorize(message, ANSI_GREEN))
449
+
450
+ def _print_warn(self, message: str) -> None:
451
+ print(colorize(message, ANSI_YELLOW))
452
+
453
+ def _print_error(self, message: str) -> None:
454
+ print(colorize(message, ANSI_RED))
455
+
394
456
  def _build_model(self, model_name: str) -> genai.GenerativeModel:
395
457
  if not self.tools:
396
458
  return genai.GenerativeModel(model_name=model_name)
@@ -408,7 +470,10 @@ class GeminiToolCLI:
408
470
  return "FLASH"
409
471
 
410
472
  def _prompt(self) -> str:
411
- return f"[{self._prompt_model_tag()}][RAG:{self.rag_status}] >> "
473
+ model_tag = colorize(self._prompt_model_tag(), ANSI_MAGENTA + ANSI_BOLD)
474
+ rag_tag = colorize(self.rag_status, ANSI_GREEN if self.rag_status == "OK" else ANSI_YELLOW)
475
+ arrow = f"{ANSI_CYAN}{ANSI_BOLD}>> {ANSI_CYAN}"
476
+ return f"[{model_tag}][RAG:{rag_tag}] {arrow}"
412
477
 
413
478
  def _switch_model_if_needed(self, model_name: str) -> None:
414
479
  if model_name == self.current_model_name:
@@ -480,28 +545,36 @@ class GeminiToolCLI:
480
545
  self.history.append({"role": role, "parts": [text]})
481
546
 
482
547
  def _print_help(self) -> None:
483
- print("Comandos: /auto, /pro, /flash, /sync, /reload-mcps, /status, /help, /exit")
548
+ self._print_info("Comandos: /auto, /pro, /flash, /sync, /reload-mcps, /status, /help, /exit")
484
549
 
485
550
  def _show_status(self) -> None:
486
551
  tools_state = "ON" if self.tools_active else "OFF"
487
552
  rag_cmd = self.rag_command or "none"
488
553
  print(
489
- f"mode={self.mode} model={self._prompt_model_tag()} rag={self.rag_status} "
490
- f"rag_cmd={rag_cmd} tools={tools_state} history={history_path_for_cwd()}"
554
+ colorize(
555
+ f"mode={self.mode} model={self._prompt_model_tag()} rag={self.rag_status} "
556
+ f"rag_cmd={rag_cmd} tools={tools_state} history={history_path_for_cwd()}",
557
+ ANSI_DIM,
558
+ )
491
559
  )
492
560
  if self.tools_error:
493
- print(f"tools_error={self.tools_error}")
561
+ self._print_warn(f"tools_error={self.tools_error}")
494
562
 
495
563
  def _resync_rag(self) -> None:
496
564
  rag_ok, rag_status = rag_sync_current_dir(self.config)
497
565
  self.rag_ok = rag_ok
498
566
  self.rag_status = rag_status
499
- print(f"RAG sync status: {self.rag_status}")
567
+ if self.rag_ok:
568
+ self._print_success(f"RAG sync status: {self.rag_status}")
569
+ else:
570
+ self._print_warn(f"RAG sync status: {self.rag_status}")
500
571
 
501
572
  def reload_mcps(self, sync_rag: bool = True, announce: bool = True) -> None:
502
573
  """Recarrega MCPs/config no inicio da sessao e sob comando manual."""
503
574
  self.config = load_config()
504
- self.rag_command = resolve_rag_command(self.config)
575
+ rag_server_command = resolve_rag_server_command(self.config)
576
+ self.rag_command = rag_server_command
577
+ self.rag_server_found = rag_server_command is not None
505
578
  self.tools = build_tools(self.config)
506
579
  self.tools_active = bool(self.tools)
507
580
  self.tools_error = ""
@@ -518,31 +591,28 @@ class GeminiToolCLI:
518
591
 
519
592
  if announce:
520
593
  rag_info = self.rag_command or "indisponivel"
521
- print(
594
+ self._print_info(
522
595
  f"MCPs recarregados. tools={'ON' if self.tools_active else 'OFF'} "
523
596
  f"rag={self.rag_status} cmd={rag_info}"
524
597
  )
525
598
 
526
599
  def run(self) -> int:
527
- print(f"Gemini Tool iniciado em: {os.path.abspath(os.getcwd())}")
528
- print(f"Historico: {history_path_for_cwd()}")
529
-
530
- if not self.rag_ok:
531
- print(
532
- "Aviso: RAG nao esta OK. Use /sync para tentar novamente ",
533
- "(ou instale own-rag-cli/own-rag).",
534
- )
600
+ self._print_info(f"{ANSI_BOLD}Gemini Tool iniciado em:{ANSI_RESET} {os.path.abspath(os.getcwd())}")
601
+ print(colorize(f"Historico: {history_path_for_cwd()}", ANSI_DIM))
602
+ if self.rag_server_found:
603
+ self._print_success("Servidor rag-codebase encontrado. Vou utiliza-lo nesta sessao.")
535
604
 
536
605
  self._print_help()
537
606
 
538
607
  while True:
539
608
  try:
540
609
  raw = input(self._prompt()).strip()
610
+ print(ANSI_RESET, end="")
541
611
  except EOFError:
542
612
  print()
543
613
  break
544
614
  except KeyboardInterrupt:
545
- print("\nInterrompido.")
615
+ self._print_warn("Interrompido.")
546
616
  break
547
617
 
548
618
  if not raw:
@@ -564,17 +634,17 @@ class GeminiToolCLI:
564
634
  continue
565
635
  if raw == "/auto":
566
636
  self.mode = "AUTO"
567
- print("Modo alterado para AUTO")
637
+ self._print_info("Modo alterado para AUTO")
568
638
  continue
569
639
  if raw == "/pro":
570
640
  self.mode = "PRO"
571
641
  self._switch_model_if_needed(MODEL_PRO)
572
- print("Modo alterado para PRO")
642
+ self._print_info("Modo alterado para PRO")
573
643
  continue
574
644
  if raw == "/flash":
575
645
  self.mode = "FLASH"
576
646
  self._switch_model_if_needed(MODEL_FLASH)
577
- print("Modo alterado para FLASH")
647
+ self._print_info("Modo alterado para FLASH")
578
648
  continue
579
649
 
580
650
  decision = self.route_for_input(raw)
@@ -585,15 +655,16 @@ class GeminiToolCLI:
585
655
  full_response = ""
586
656
  try:
587
657
  stream = self.chat.send_message(raw, stream=True)
658
+ print(ANSI_GREEN, end="")
588
659
  for chunk in stream:
589
660
  piece = self._extract_text(chunk)
590
661
  if piece:
591
662
  full_response += piece
592
663
  print(piece, end="", flush=True)
593
- print()
664
+ print(ANSI_RESET)
594
665
  emit_plin()
595
666
  except Exception as exc:
596
- print(f"Erro ao gerar resposta: {exc}")
667
+ self._print_error(f"Erro ao gerar resposta: {exc}")
597
668
  self.history.pop()
598
669
  emit_plin()
599
670
  continue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemini-cli-pro",
3
- "version": "0.0.4-snapshot",
3
+ "version": "0.0.7-snapshot",
4
4
  "description": "Gemini CLI em Python com sync de RAG local e roteamento Flash/Pro",
5
5
  "license": "MIT",
6
6
  "bin": {