"""Gemini CLI backend.""" import json import os import shutil from tai_pkg.clirun import Cli from tai_pkg.errors import BackendError from tai_pkg.opencode import Opencode __all__ = ["Gemini"] class Gemini: """Gemini.""" @staticmethod def gemini_binary(): return os.environ.get("GEMINI_BIN") or shutil.which("gemini") @staticmethod def gemini_parse_output(stdout): try: data = json.loads(stdout or "") except ValueError: return Opencode.opencode_parse(stdout) if not isinstance(data, dict): return Opencode.opencode_parse(stdout) err = data.get("error") if err: raise BackendError("gemini: %s" % (json.dumps(err) if isinstance(err, dict) else str(err))[:300]) response = data.get("response") if isinstance(response, str) and response.strip(): return Opencode.opencode_parse(response) return Opencode.opencode_parse(stdout) @staticmethod def gemini_run(prompt, timeout): binary = Gemini.gemini_binary() if not binary: raise BackendError("gemini CLI not found (GEMINI_BIN or PATH)") base = [binary, "-p", prompt] variants = [ base + ["--output-format", "json", "--approval-mode", "plan"], base + ["--output-format", "json"], base, ] last_error = "" for argv in variants: done = Cli._cli_run(argv, None, timeout, "gemini") if done.returncode != 0 and Cli._cli_unknown_flag(done.stderr) and argv is not variants[-1]: last_error = (done.stderr or "").strip()[:200] continue if done.returncode != 0 and not (done.stdout or "").strip(): blob = ((done.stderr or "") + "\n" + (done.stdout or "")).strip() if Cli._cli_auth_failure(blob): raise BackendError("gemini not authenticated (run gemini login or set GEMINI_API_KEY): %s" % blob[:200]) hint = {42: "input error", 53: "turn limit exceeded"}.get(done.returncode, "") raise BackendError("gemini exit %d%s: %s" % (done.returncode, " (%s)" % hint if hint else "", blob[:300] or last_error or "no output")) if not (done.stdout or "").strip(): blob = (done.stderr or "").strip() raise BackendError("gemini returned no output: %s" % (blob[:300] or last_error or "empty stdout")) return done.stdout or "" raise BackendError("gemini failed: %s" % (last_error or "no usable output")) @staticmethod def gemini_health(): binary = Gemini.gemini_binary() if not binary: return "missing: gemini CLI not found" return Cli._cli_version_only_health(binary, "gemini")