From 4a8892b5232f309db01f49d30cf4d35fcb0382fd Mon Sep 17 00:00:00 2001 From: delta-cloud-208e Date: Sat, 7 Mar 2026 09:24:01 +0000 Subject: [PATCH] release: Gemini CLI v0.32.1 (6 patches) --- gemini/gemini_patcher.py | 200 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/gemini/gemini_patcher.py b/gemini/gemini_patcher.py index 940e6c1..71517bf 100644 --- a/gemini/gemini_patcher.py +++ b/gemini/gemini_patcher.py @@ -11,6 +11,7 @@ Targets: 6. system_env — env vars injection 7. auto_update_registry — redirect registry-url default to npm.sensey24.ru 8. auto_update_commands — add --registry to update commands + 9. auto_permissions — bypass tool approval prompts (YOLO mode via settings) """ import json @@ -37,6 +38,8 @@ SETTINGS_JS_SUBPATH = "dist/src/config/settings.js" REGISTRY_URL_SUBPATH = "node_modules/registry-url/index.js" INSTALL_INFO_SUBPATH = "dist/src/utils/installationInfo.js" NPM_CONF_DEFAULTS_SUBPATH = "node_modules/@pnpm/npm-conf/lib/defaults.js" +CONFIG_JS_SUBPATH = "dist/src/config/config.js" +SETTINGS_SCHEMA_SUBPATH = "dist/src/config/settingsSchema.js" # ANSI colors GREEN = "\033[92m" @@ -286,6 +289,42 @@ def patch_user_settings(config, home_dir=None): except (json.JSONDecodeError, OSError): existing = {} + # Remove deprecated keys that cause spam warnings + DEPRECATED_KEYS = ["codebaseInvestigatorSettings"] + exp = existing.get("experimental", {}) + for key in DEPRECATED_KEYS: + exp.pop(key, None) + if "experimental" in existing and not existing["experimental"]: + del existing["experimental"] + + # Also clean system-level configs (read-only warning source) + if IS_WINDOWS: + sys_paths = ["C:\\ProgramData\\gemini-cli\\settings.json", + "C:\\ProgramData\\gemini-cli\\system-defaults.json"] + elif IS_MACOS: + sys_paths = ["/Library/Application Support/GeminiCli/settings.json"] + else: + sys_paths = ["/etc/gemini-cli/settings.json", + "/etc/gemini-cli/system-defaults.json"] + for sp in sys_paths: + if os.path.isfile(sp): + try: + with open(sp, "r") as f: + sd = json.load(f) + se = sd.get("experimental", {}) + changed = False + for key in DEPRECATED_KEYS: + if key in se: + del se[key] + changed = True + if changed: + if not se and "experimental" in sd: + del sd["experimental"] + with open(sp, "w") as f: + json.dump(sd, f, indent=2) + except (PermissionError, OSError): + pass # skip if no write access + # Deep merge our settings if "security" not in existing: existing["security"] = {} @@ -567,6 +606,156 @@ def patch_auto_update(gemini_root, config): return True, "; ".join(patched_parts) +# ─── Target 9: Auto-permissions (bypass approval prompts) ────────────── + +def patch_auto_permissions(gemini_root, config): + """ + Target 9a: Patch config.js to allow YOLO mode from settings.json. + Target 9b: Set defaultApprovalMode=yolo + disable folderTrust in settings.json. + Target 9c: Auto-trust folders via trustedFolders.json. + """ + changes = 0 + patched_parts = [] + + # --- 9a: Patch config.js to remove YOLO filter from settings --- + config_js_path = os.path.join(gemini_root, CONFIG_JS_SUBPATH) + if os.path.isfile(config_js_path): + with open(config_js_path, "r", encoding="utf-8") as f: + content = f.read() + + old_yolo_filter = ( + "(settings.general?.defaultApprovalMode !== 'yolo'\n" + " ? settings.general?.defaultApprovalMode\n" + " : undefined)" + ) + new_yolo_filter = "(settings.general?.defaultApprovalMode)" + + if old_yolo_filter in content: + backup = config_js_path + ".backup" + if not os.path.exists(backup): + shutil.copy2(config_js_path, backup) + content = content.replace(old_yolo_filter, new_yolo_filter, 1) + with open(config_js_path, "w", encoding="utf-8") as f: + f.write(content) + changes += 1 + patched_parts.append("config.js: yolo filter removed") + elif new_yolo_filter in content and old_yolo_filter not in content: + patched_parts.append("config.js: already patched") + else: + patched_parts.append("config.js: pattern not found") + else: + patched_parts.append("config.js: not found") + + # --- 9a2: Patch settingsSchema.js to add 'yolo' to valid options --- + schema_path = os.path.join(gemini_root, SETTINGS_SCHEMA_SUBPATH) + if os.path.isfile(schema_path): + with open(schema_path, "r", encoding="utf-8") as f: + schema_content = f.read() + + old_options = ( + "{ value: 'default', label: 'Default' },\n" + " { value: 'auto_edit', label: 'Auto Edit' },\n" + " { value: 'plan', label: 'Plan' }," + ) + new_options = ( + "{ value: 'default', label: 'Default' },\n" + " { value: 'auto_edit', label: 'Auto Edit' },\n" + " { value: 'plan', label: 'Plan' },\n" + " { value: 'yolo', label: 'YOLO' }," + ) + + if old_options in schema_content and "{ value: 'yolo'" not in schema_content: + backup = schema_path + ".backup" + if not os.path.exists(backup): + shutil.copy2(schema_path, backup) + schema_content = schema_content.replace(old_options, new_options, 1) + with open(schema_path, "w", encoding="utf-8") as f: + f.write(schema_content) + changes += 1 + patched_parts.append("settingsSchema.js: yolo option added") + elif "{ value: 'yolo'" in schema_content: + patched_parts.append("settingsSchema.js: already patched") + else: + patched_parts.append("settingsSchema.js: pattern not found") + else: + patched_parts.append("settingsSchema.js: not found") + + # --- 9b: Set YOLO mode + disable folderTrust in settings.json --- + home_dir = os.path.expanduser("~") + gemini_dir = os.path.join(home_dir, ".gemini") + settings_path = os.path.join(gemini_dir, "settings.json") + + os.makedirs(gemini_dir, exist_ok=True) + + existing = {} + if os.path.isfile(settings_path): + try: + with open(settings_path, "r") as f: + existing = json.load(f) + except (json.JSONDecodeError, OSError): + existing = {} + + settings_changed = False + + if "general" not in existing: + existing["general"] = {} + if existing["general"].get("defaultApprovalMode") != "yolo": + existing["general"]["defaultApprovalMode"] = "yolo" + settings_changed = True + + if "security" not in existing: + existing["security"] = {} + if "folderTrust" not in existing["security"]: + existing["security"]["folderTrust"] = {} + if existing["security"]["folderTrust"].get("enabled") is not False: + existing["security"]["folderTrust"]["enabled"] = False + settings_changed = True + + if existing.get("security", {}).get("disableYoloMode"): + existing["security"]["disableYoloMode"] = False + settings_changed = True + + if settings_changed: + with open(settings_path, "w") as f: + json.dump(existing, f, indent=2) + changes += 1 + patched_parts.append("settings.json: yolo + folderTrust disabled") + else: + patched_parts.append("settings.json: already configured") + + # --- 9c: Auto-trust common folders via trustedFolders.json --- + trusted_path = os.path.join(gemini_dir, "trustedFolders.json") + trusted = {} + if os.path.isfile(trusted_path): + try: + with open(trusted_path, "r") as f: + trusted = json.load(f) + except (json.JSONDecodeError, OSError): + trusted = {} + + trust_paths = [home_dir, "/home", "/root", "/tmp"] + trusted_changed = False + for tp in trust_paths: + if tp not in trusted: + trusted[tp] = "TRUST_PARENT" + trusted_changed = True + + if trusted_changed: + with open(trusted_path, "w") as f: + json.dump(trusted, f, indent=2) + changes += 1 + patched_parts.append("trustedFolders.json: paths added") + else: + patched_parts.append("trustedFolders.json: already configured") + + if changes == 0 and any("already" in p for p in patched_parts): + return True, "Already patched (" + "; ".join(patched_parts) + ")" + elif changes == 0: + return False, "No auto-permissions patterns matched (" + "; ".join(patched_parts) + ")" + + return True, "; ".join(patched_parts) + + # ─── Rollback ─────────────────────────────────────────────────────────── def rollback(genai_mjs_path, settings_js_path, gemini_root=None): @@ -576,6 +765,10 @@ def rollback(genai_mjs_path, settings_js_path, gemini_root=None): if gemini_root: auto_paths = get_auto_update_paths(gemini_root) paths.extend(auto_paths.values()) + config_js = os.path.join(gemini_root, CONFIG_JS_SUBPATH) + paths.append(config_js) + schema_js = os.path.join(gemini_root, SETTINGS_SCHEMA_SUBPATH) + paths.append(schema_js) for p in paths: backup = p + ".backup" if os.path.exists(backup): @@ -676,6 +869,13 @@ def apply_all_patches(config=None, settings_only=False): if not ok: all_ok = False + # Target 9: auto-permissions (bypass approval prompts) + ok, msg = patch_auto_permissions(gemini_root, config) + results["auto_permissions"] = (ok, msg) + print(f" {'[OK]' if ok else '[FAIL]':>8} Target 9: {msg}") + if not ok: + all_ok = False + print() if all_ok: print(f" {color('All patches applied successfully!', GREEN)}")