{"slug": "migrate-antigravity-py", "title": "migrate_antigravity.py", "summary": "Antigravity released a migration tool to automatically transfer extensions, settings, and conversation histories from Antigravity v1 to the new Antigravity IDE. The script handles process termination, file migration, and logging to ensure a smooth transition.", "body_md": "| #!/usr/bin/env python3 | |\n| \"\"\" | |\n| Antigravity to Antigravity IDE Migration Tool | |\n| ============================================== | |\n| A self-contained script to automatically migrate extensions, custom settings, keybindings, | |\n| snippets, workspace states, and entire conversation histories from Antigravity v1 | |\n| to the new Antigravity IDE. | |\n| Author: Antigravity AI Coding Assistant (pair-programmed with USER) | |\n| License: MIT | |\n| \"\"\" | |\n| import os | |\n| import sys | |\n| import time | |\n| import json | |\n| import shutil | |\n| import sqlite3 | |\n| import base64 | |\n| import signal | |\n| import glob | |\n| import subprocess | |\n| # Define base paths dynamically based on user's home folder | |\n| HOME = os.path.expanduser(\"~\") | |\n| OLD_DOT_ANTIGRAVITY = os.path.join(HOME, \".antigravity\") | |\n| NEW_DOT_ANTIGRAVITY = os.path.join(HOME, \".antigravity-ide\") | |\n| OLD_DOT_GEMINI = os.path.join(HOME, \".gemini/antigravity\") | |\n| NEW_DOT_GEMINI = os.path.join(HOME, \".gemini/antigravity-ide\") | |\n| OLD_SUPPORT = os.path.join(HOME, \"Library/Application Support/Antigravity\") | |\n| NEW_SUPPORT = os.path.join(HOME, \"Library/Application Support/Antigravity IDE\") | |\n| LOG_FILE = os.path.join(NEW_DOT_GEMINI, \"migration_run.log\") | |\n| def log(msg, level=\"INFO\"): | |\n| timestamp = time.strftime('%Y-%m-%d %H:%M:%S') | |\n| formatted_msg = f\"[{timestamp}] [{level}] {msg}\" | |\n| print(formatted_msg) | |\n| try: | |\n| # Create directory if it doesn't exist | |\n| os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True) | |\n| with open(LOG_FILE, \"a\", encoding=\"utf-8\") as f: | |\n| f.write(formatted_msg + \"\\n\") | |\n| except Exception: | |\n| pass | |\n| def kill_running_processes(): | |\n| log(\"Scanning and force-terminating any running Antigravity processes...\") | |\n| my_pid = os.getpid() | |\n| killed_any = False | |\n| # 1. Broad pgrep/pkill attempts | |\n| try: | |\n| subprocess.run([\"pkill\", \"-f\", \"Antigravity IDE\"], stderr=subprocess.DEVNULL) | |\n| subprocess.run([\"pkill\", \"-f\", \"Antigravity\"], stderr=subprocess.DEVNULL) | |\n| except Exception: | |\n| pass | |\n| # 2. Precise manual process scanning | |\n| try: | |\n| output = subprocess.check_output([\"ps\", \"-ef\"]).decode('utf-8', errors='ignore') | |\n| for line in output.splitlines(): | |\n| if (\"Antigravity\" in line or \"Antigravity IDE\" in line) and \"grep\" not in line and \"migrate_antigravity.py\" not in line: | |\n| parts = line.split() | |\n| if len(parts) > 1: | |\n| pid = int(parts[1]) | |\n| if pid != my_pid: | |\n| try: | |\n| os.kill(pid, signal.SIGKILL) | |\n| log(f\"Terminated process {pid}: {' '.join(parts[7:])}\", \"PROCESS\") | |\n| killed_any = True | |\n| except Exception: | |\n| pass | |\n| except Exception as e: | |\n| log(f\"Error checking processes: {e}\", \"WARNING\") | |\n| if killed_any: | |\n| log(\"Successfully terminated active processes. Waiting 3 seconds for file locks to release...\") | |\n| time.sleep(3) | |\n| else: | |\n| log(\"No active Antigravity or Antigravity IDE processes found.\") | |\n| def migrate_extensions(): | |\n| log(\"Migrating extensions...\") | |\n| old_ext_dir = os.path.join(OLD_DOT_ANTIGRAVITY, \"extensions\") | |\n| new_ext_dir = os.path.join(NEW_DOT_ANTIGRAVITY, \"extensions\") | |\n| old_json_path = os.path.join(old_ext_dir, \"extensions.json\") | |\n| new_json_path = os.path.join(new_ext_dir, \"extensions.json\") | |\n| if not os.path.exists(old_json_path): | |\n| log(\"Old extensions.json not found. Skipping extension migration.\", \"WARNING\") | |\n| return | |\n| os.makedirs(new_ext_dir, exist_ok=True) | |\n| if not os.path.exists(new_json_path): | |\n| with open(new_json_path, \"w\", encoding=\"utf-8\") as f: | |\n| f.write(\"[]\") | |\n| try: | |\n| with open(old_json_path, \"r\", encoding=\"utf-8\") as f: | |\n| old_exts = json.load(f) | |\n| with open(new_json_path, \"r\", encoding=\"utf-8\") as f: | |\n| new_exts = json.load(f) | |\n| except Exception as e: | |\n| log(f\"Failed to read extensions JSON files: {e}\", \"ERROR\") | |\n| return | |\n| new_exts_dict = {ext.get(\"identifier\", {}).get(\"id\", \"\").lower(): ext for ext in new_exts if ext.get(\"identifier\")} | |\n| added_count = 0 | |\n| copied_dirs = 0 | |\n| for old_ext in old_exts: | |\n| identifier = old_ext.get(\"identifier\", {}) | |\n| ext_id = identifier.get(\"id\", \"\").lower() | |\n| if not ext_id: | |\n| continue | |\n| if ext_id in new_exts_dict: | |\n| log(f\"Extension '{ext_id}' is already installed in target. Skipping registration.\") | |\n| continue | |\n| # Clone data and rewrite directory paths | |\n| migrated_ext = json.loads(json.dumps(old_ext)) | |\n| if \"location\" in migrated_ext and \"path\" in migrated_ext[\"location\"]: | |\n| migrated_ext[\"location\"][\"path\"] = migrated_ext[\"location\"][\"path\"].replace(OLD_DOT_ANTIGRAVITY, NEW_DOT_ANTIGRAVITY) | |\n| if \"location\" in migrated_ext and \"fsPath\" in migrated_ext[\"location\"]: | |\n| migrated_ext[\"location\"][\"fsPath\"] = migrated_ext[\"location\"][\"fsPath\"].replace(OLD_DOT_ANTIGRAVITY, NEW_DOT_ANTIGRAVITY) | |\n| if \"location\" in migrated_ext and \"external\" in migrated_ext[\"location\"]: | |\n| migrated_ext[\"location\"][\"external\"] = migrated_ext[\"location\"][\"external\"].replace(OLD_DOT_ANTIGRAVITY, NEW_DOT_ANTIGRAVITY) | |\n| # Copy directory if it exists | |\n| rel_loc = old_ext.get(\"relativeLocation\") | |\n| if rel_loc: | |\n| src_dir = os.path.join(old_ext_dir, rel_loc) | |\n| dst_dir = os.path.join(new_ext_dir, rel_loc) | |\n| if os.path.exists(src_dir): | |\n| if not os.path.exists(dst_dir): | |\n| log(f\"Copying extension directory: {rel_loc}\") | |\n| try: | |\n| shutil.copytree(src_dir, dst_dir, symlinks=True) | |\n| copied_dirs += 1 | |\n| except Exception as e: | |\n| log(f\"Failed to copy directory {rel_loc}: {e}\", \"ERROR\") | |\n| else: | |\n| log(f\"Extension directory '{rel_loc}' already exists in target.\") | |\n| else: | |\n| log(f\"Listed extension directory not found at: {src_dir}\", \"WARNING\") | |\n| new_exts.append(migrated_ext) | |\n| added_count += 1 | |\n| try: | |\n| with open(new_json_path, \"w\", encoding=\"utf-8\") as f: | |\n| json.dump(new_exts, f, indent=None) | |\n| log(f\"Successfully migrated {added_count} extension entries and copied {copied_dirs} folders.\") | |\n| except Exception as e: | |\n| log(f\"Failed to write merged extensions.json: {e}\", \"ERROR\") | |\n| def merge_vscdb(src_db, dst_db, merge_protobufs=False): | |\n| log(f\"Merging database: {os.path.basename(src_db)}\") | |\n| if not os.path.exists(src_db): | |\n| return | |\n| os.makedirs(os.path.dirname(dst_db), exist_ok=True) | |\n| # 1. Read destination (target) keys | |\n| dst_records = {} | |\n| if os.path.exists(dst_db): | |\n| try: | |\n| conn = sqlite3.connect(dst_db) | |\n| cursor = conn.cursor() | |\n| cursor.execute(\"SELECT key, value FROM ItemTable\") | |\n| for row in cursor.fetchall(): | |\n| dst_records[row[0]] = row[1] | |\n| conn.close() | |\n| except Exception as e: | |\n| log(f\"Failed to read existing keys from target DB: {e}\", \"WARNING\") | |\n| # 2. Backup target DB | |\n| if os.path.exists(dst_db): | |\n| backup_path = dst_db + \".migration_backup\" | |\n| try: | |\n| shutil.copy2(dst_db, backup_path) | |\n| except Exception as e: | |\n| log(f\"Failed to backup target DB: {e}\", \"WARNING\") | |\n| # 3. Read source (old) keys | |\n| src_records = {} | |\n| try: | |\n| conn = sqlite3.connect(src_db) | |\n| cursor = conn.cursor() | |\n| cursor.execute(\"SELECT key, value FROM ItemTable\") | |\n| for row in cursor.fetchall(): | |\n| src_records[row[0]] = row[1] | |\n| conn.close() | |\n| except Exception as e: | |\n| log(f\"Failed to read keys from source DB: {e}\", \"ERROR\") | |\n| return | |\n| # 4. Merge records | |\n| merged_records = src_records.copy() | |\n| if merge_protobufs: | |\n| # Special base64 protobuf concatenation merge for conversation histories | |\n| protobuf_keys = [ | |\n| \"antigravityUnifiedStateSync.trajectorySummaries\", | |\n| \"antigravityUnifiedStateSync.sidebarWorkspaces\" | |\n| ] | |\n| for key in dst_records: | |\n| if key in protobuf_keys and key in src_records: | |\n| try: | |\n| src_bytes = base64.b64decode(src_records[key]) | |\n| dst_bytes = base64.b64decode(dst_records[key]) | |\n| # Protobuf repeated fields can be merged by simple binary concatenation! | |\n| merged_bytes = src_bytes + dst_bytes | |\n| merged_val = base64.b64encode(merged_bytes).decode('utf-8') | |\n| merged_records[key] = merged_val | |\n| log(f\"Concatenated protobuf state key: {key} ({len(src_bytes)}B source + {len(dst_bytes)}B target)\") | |\n| except Exception as e: | |\n| log(f\"Failed to merge protobuf key '{key}': {e}. Overwriting with target value.\", \"WARNING\") | |\n| merged_records[key] = dst_records[key] | |\n| else: | |\n| merged_records[key] = dst_records[key] | |\n| else: | |\n| # Standard merge: let target (current) session keys override source keys on conflict | |\n| merged_records.update(dst_records) | |\n| # 5. Write back to destination database | |\n| try: | |\n| # Copy schema/DB first to preserve indices/SQLite page configurations | |\n| if os.path.exists(src_db): | |\n| shutil.copy2(src_db, dst_db) | |\n| conn = sqlite3.connect(dst_db) | |\n| cursor = conn.cursor() | |\n| cursor.execute(\"CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)\") | |\n| cursor.execute(\"DELETE FROM ItemTable\") | |\n| for key, val in merged_records.items(): | |\n| cursor.execute(\"INSERT OR REPLACE INTO ItemTable (key, value) VALUES (?, ?)\", (key, val)) | |\n| conn.commit() | |\n| conn.close() | |\n| log(f\"Wrote {len(merged_records)} keys to database successfully.\") | |\n| except Exception as e: | |\n| log(f\"Failed to write merged database: {e}\", \"ERROR\") | |\n| def merge_json_files(src_path, dst_path): | |\n| if not os.path.exists(src_path): | |\n| return | |\n| os.makedirs(os.path.dirname(dst_path), exist_ok=True) | |\n| dst_data = {} | |\n| if os.path.exists(dst_path): | |\n| try: | |\n| with open(dst_path, \"r\", encoding=\"utf-8\") as f: | |\n| dst_data = json.load(f) | |\n| except Exception: | |\n| pass | |\n| try: | |\n| with open(src_path, \"r\", encoding=\"utf-8\") as f: | |\n| src_data = json.load(f) | |\n| except Exception as e: | |\n| log(f\"Failed to read source JSON {src_path}: {e}\", \"ERROR\") | |\n| return | |\n| merged_data = src_data.copy() | |\n| merged_data.update(dst_data) # Target overrides source on conflict | |\n| try: | |\n| with open(dst_path, \"w\", encoding=\"utf-8\") as f: | |\n| json.dump(merged_data, f, indent=2) | |\n| except Exception as e: | |\n| log(f\"Failed to save merged JSON {dst_path}: {e}\", \"ERROR\") | |\n| def migrate_app_support(): | |\n| log(\"Migrating User settings, keybindings, snippets, and workspace states...\") | |\n| old_user_dir = os.path.join(OLD_SUPPORT, \"User\") | |\n| new_user_dir = os.path.join(NEW_SUPPORT, \"User\") | |\n| # 1. Merge settings.json and copy keybindings.json | |\n| merge_json_files(os.path.join(old_user_dir, \"settings.json\"), os.path.join(new_user_dir, \"settings.json\")) | |\n| src_kb = os.path.join(old_user_dir, \"keybindings.json\") | |\n| dst_kb = os.path.join(new_user_dir, \"keybindings.json\") | |\n| if os.path.exists(src_kb): | |\n| shutil.copy2(src_kb, dst_kb) | |\n| # 2. Copy custom user snippets | |\n| src_snippets = os.path.join(old_user_dir, \"snippets\") | |\n| dst_snippets = os.path.join(new_user_dir, \"snippets\") | |\n| if os.path.exists(src_snippets) and not os.path.exists(dst_snippets): | |\n| try: | |\n| shutil.copytree(src_snippets, dst_snippets) | |\n| except Exception as e: | |\n| log(f\"Failed to copy snippets: {e}\", \"WARNING\") | |\n| # 3. Copy workspaces and shared_proto_db directories | |\n| for folder in [\"Workspaces\", \"shared_proto_db\"]: | |\n| src_f = os.path.join(OLD_SUPPORT, folder) | |\n| dst_f = os.path.join(NEW_SUPPORT, folder) | |\n| if os.path.exists(src_f) and not os.path.exists(dst_f): | |\n| try: | |\n| shutil.copytree(src_f, dst_f) | |\n| log(f\"Copied root storage folder: {folder}\") | |\n| except Exception as e: | |\n| log(f\"Failed to copy folder {folder}: {e}\", \"WARNING\") | |\n| # 4. Migrate globalStorage databases (with protobuf merge for trajectory/sidebar keys) | |\n| old_gs = os.path.join(old_user_dir, \"globalStorage\") | |\n| new_gs = os.path.join(new_user_dir, \"globalStorage\") | |\n| if os.path.exists(old_gs): | |\n| os.makedirs(new_gs, exist_ok=True) | |\n| for item in os.listdir(old_gs): | |\n| src_item = os.path.join(old_gs, item) | |\n| dst_item = os.path.join(new_gs, item) | |\n| if item == \"state.vscdb\": | |\n| merge_vscdb(src_item, dst_item, merge_protobufs=True) | |\n| elif item == \"state.vscdb.backup\": | |\n| shutil.copy2(src_item, dst_item) | |\n| elif item == \"storage.json\": | |\n| merge_json_files(src_item, dst_item) | |\n| else: | |\n| if os.path.isdir(src_item) and not os.path.exists(dst_item): | |\n| shutil.copytree(src_item, dst_item) | |\n| elif os.path.isfile(src_item) and not os.path.exists(dst_item): | |\n| shutil.copy2(src_item, dst_item) | |\n| # 5. Migrate workspaceStorage databases (individual folders) | |\n| old_ws = os.path.join(old_user_dir, \"workspaceStorage\") | |\n| new_ws = os.path.join(new_user_dir, \"workspaceStorage\") | |\n| if os.path.exists(old_ws): | |\n| os.makedirs(new_ws, exist_ok=True) | |\n| for workspace_id in os.listdir(old_ws): | |\n| src_w = os.path.join(old_ws, workspace_id) | |\n| dst_w = os.path.join(new_ws, workspace_id) | |\n| if not os.path.isdir(src_w): | |\n| continue | |\n| if not os.path.exists(dst_w): | |\n| try: | |\n| shutil.copytree(src_w, dst_w) | |\n| except Exception as e: | |\n| log(f\"Failed to copy workspace storage {workspace_id}: {e}\", \"WARNING\") | |\n| else: | |\n| # Target exists: Merge the inner state database | |\n| merge_vscdb(os.path.join(src_w, \"state.vscdb\"), os.path.join(dst_w, \"state.vscdb\")) | |\n| src_backup = os.path.join(src_w, \"state.vscdb.backup\") | |\n| dst_backup = os.path.join(dst_w, \"state.vscdb.backup\") | |\n| if os.path.exists(src_backup): | |\n| shutil.copy2(src_backup, dst_backup) | |\n| # Merge missing items | |\n| for file_item in os.listdir(src_w): | |\n| if file_item not in [\"state.vscdb\", \"state.vscdb.backup\"]: | |\n| src_f = os.path.join(src_w, file_item) | |\n| dst_f = os.path.join(dst_w, file_item) | |\n| if os.path.isdir(src_f) and not os.path.exists(dst_f): | |\n| shutil.copytree(src_f, dst_f) | |\n| elif os.path.isfile(src_f) and not os.path.exists(dst_f): | |\n| shutil.copy2(src_f, dst_f) | |\n| def migrate_gemini_configs(): | |\n| log(\"Migrating Gemini assistant states & conversation logs...\") | |\n| os.makedirs(NEW_DOT_GEMINI, exist_ok=True) | |\n| # 1. Copy antigravity_state.pbtxt | |\n| src_state = os.path.join(OLD_DOT_GEMINI, \"antigravity_state.pbtxt\") | |\n| dst_state = os.path.join(NEW_DOT_GEMINI, \"antigravity_state.pbtxt\") | |\n| if os.path.exists(src_state): | |\n| shutil.copy2(src_state, dst_state) | |\n| # 2. Copy agyhub_summaries_proto.pb | |\n| src_summary = os.path.join(OLD_DOT_GEMINI, \"agyhub_summaries_proto.pb\") | |\n| dst_summary = os.path.join(NEW_DOT_GEMINI, \"agyhub_summaries_proto.pb\") | |\n| if os.path.exists(src_summary): | |\n| shutil.copy2(src_summary, dst_summary) | |\n| # 3. Setup MCP config symlink | |\n| target_mcp = os.path.join(NEW_DOT_GEMINI, \"mcp_config.json\") | |\n| shared_mcp = os.path.join(HOME, \".gemini/config/mcp_config.json\") | |\n| if os.path.exists(target_mcp) and not os.path.islink(target_mcp): | |\n| if os.path.getsize(target_mcp) == 0: | |\n| try: | |\n| os.remove(target_mcp) | |\n| os.symlink(shared_mcp, target_mcp) | |\n| log(\"Replaced empty target mcp_config.json with symlink to shared config.\") | |\n| except Exception as e: | |\n| log(f\"Failed to symlink mcp_config: {e}\", \"WARNING\") | |\n| elif not os.path.exists(target_mcp) and os.path.exists(shared_mcp): | |\n| try: | |\n| os.symlink(shared_mcp, target_mcp) | |\n| log(\"Created symlink for shared MCP configuration.\") | |\n| except Exception as e: | |\n| log(f\"Failed to create symlink for mcp_config: {e}\", \"WARNING\") | |\n| # 4. Copy raw conversation protobuf (.pb) log files | |\n| src_convo_dir = os.path.join(OLD_DOT_GEMINI, \"conversations\") | |\n| dst_convo_dir = os.path.join(NEW_DOT_GEMINI, \"conversations\") | |\n| if os.path.exists(src_convo_dir): | |\n| os.makedirs(dst_convo_dir, exist_ok=True) | |\n| pb_copied = 0 | |\n| for f in os.listdir(src_convo_dir): | |\n| if f.endswith(\".pb\"): | |\n| src_file = os.path.join(src_convo_dir, f) | |\n| dst_file = os.path.join(dst_convo_dir, f) | |\n| if not os.path.exists(dst_file): | |\n| try: | |\n| shutil.copy2(src_file, dst_file) | |\n| pb_copied += 1 | |\n| except Exception as e: | |\n| log(f\"Failed to copy convo file {f}: {e}\", \"WARNING\") | |\n| log(f\"Copied {pb_copied} raw conversation protobuf logs (.pb).\") | |\n| def configure_pbtxt_states(status): | |\n| log(f\"Setting pbtxt migration states to {status}...\") | |\n| state_files = [ | |\n| os.path.join(OLD_DOT_GEMINI, \"antigravity_state.pbtxt\"), | |\n| os.path.join(NEW_DOT_GEMINI, \"antigravity_state.pbtxt\") | |\n| ] | |\n| for state_file in state_files: | |\n| if os.path.exists(state_file): | |\n| try: | |\n| with open(state_file, \"r\", encoding=\"utf-8\") as f: | |\n| content = f.read() | |\n| if status == \"UNSPECIFIED\": | |\n| content = content.replace(\"migrate_convos_into_projects: MIGRATION_STATUS_COMPLETED\", | |\n| \"migrate_convos_into_projects: MIGRATION_STATUS_UNSPECIFIED\") | |\n| else: | |\n| content = content.replace(\"migrate_convos_into_projects: MIGRATION_STATUS_UNSPECIFIED\", | |\n| \"migrate_convos_into_projects: MIGRATION_STATUS_COMPLETED\") | |\n| with open(state_file, \"w\", encoding=\"utf-8\") as f: | |\n| f.write(content) | |\n| log(f\"Successfully configured state file: {os.path.basename(state_file)}\") | |\n| except Exception as e: | |\n| log(f\"Failed to configure state file {state_file}: {e}\", \"ERROR\") | |\n| def run_language_server_migration(): | |\n| log(\"Locating language server binary inside Antigravity IDE app bundle...\") | |\n| bin_pattern = \"/Applications/Antigravity IDE.app/Contents/Resources/app/extensions/antigravity/bin/language_server_macos_*\" | |\n| binaries = glob.glob(bin_pattern) | |\n| if not binaries: | |\n| log(\"No matching language server binary found! Please make sure Antigravity IDE is installed in /Applications.\", \"ERROR\") | |\n| return False | |\n| ls_binary = binaries[0] | |\n| log(f\"Using language server binary: {ls_binary}\") | |\n| log(\"Spawning standalone language server to process conversation migrations...\") | |\n| try: | |\n| proc = subprocess.Popen([ | |\n| ls_binary, | |\n| \"--standalone\", | |\n| \"--app_data_dir\", \"antigravity-ide\" | |\n| ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |\n| log(f\"Standalone language server successfully spawned (PID: {proc.pid}). Running migration pass...\") | |\n| time.sleep(8) # Allow the Go server sufficient time to run migrations offline | |\n| proc.terminate() | |\n| proc.wait(timeout=2) | |\n| log(\"Standalone language server completed processing and shut down cleanly.\") | |\n| return True | |\n| except subprocess.TimeoutExpired: | |\n| try: | |\n| proc.kill() | |\n| except Exception: | |\n| pass | |\n| log(\"Language server process took too long to exit and was forced terminated.\", \"WARNING\") | |\n| return True | |\n| except Exception as e: | |\n| log(f\"Failed to execute standalone conversation migration: {e}\", \"ERROR\") | |\n| return False | |\n| def count_converted_chats(): | |\n| dst_convo_dir = os.path.join(NEW_DOT_GEMINI, \"conversations\") | |\n| if not os.path.exists(dst_convo_dir): | |\n| return 0 | |\n| try: | |\n| db_files = [f for f in os.listdir(dst_convo_dir) if f.endswith(\".db\") and len(f) > 36] | |\n| return len(db_files) | |\n| except Exception: | |\n| return 0 | |\n| def reopen_ide(): | |\n| log(\"Reopening Antigravity IDE...\") | |\n| try: | |\n| subprocess.Popen([\"open\", \"/Applications/Antigravity IDE.app\"]) | |\n| log(\"Open command issued successfully. IDE is launching.\") | |\n| except Exception as e: | |\n| log(f\"Failed to automatically restart IDE: {e}. You may start it manually.\", \"WARNING\") | |\n| def main(): | |\n| print(r\"\"\" | |\n| ============================================================ | |\n| ANTIGRAVITY TO ANTIGRAVITY IDE MIGRATION TOOL | |\n| ============================================================ | |\n| This script safely restores your extensions, custom settings, | |\n| keybindings, snippets, custom workspaces, and conversation | |\n| history into the new Antigravity IDE. | |\n| ============================================================ | |\n| \"\"\") | |\n| # Pre-checks | |\n| if not os.path.exists(OLD_DOT_GEMINI): | |\n| print(f\"[-] Error: Could not locate old Antigravity config directory at {OLD_DOT_GEMINI}\") | |\n| print(\" Make sure Antigravity v1 was installed on this machine.\") | |\n| sys.exit(1) | |\n| response = input(\"[?] Proceed with offline migration? (This will force quit Antigravity) [y/N]: \") | |\n| if response.strip().lower() not in [\"y\", \"yes\"]: | |\n| print(\"[-] Migration cancelled by user.\") | |\n| sys.exit(0) | |\n| print(\"\\n[1/7] Terminating running processes...\") | |\n| kill_running_processes() | |\n| print(\"\\n[2/7] Migrating extensions...\") | |\n| migrate_extensions() | |\n| print(\"\\n[3/7] Migrating User settings, keybindings and layout states...\") | |\n| migrate_app_support() | |\n| print(\"\\n[4/7] Copying logs and configuration metadata...\") | |\n| migrate_gemini_configs() | |\n| print(\"\\n[5/7] Triggering Go-based conversation migration module...\") | |\n| configure_pbtxt_states(\"UNSPECIFIED\") | |\n| ls_success = run_language_server_migration() | |\n| # Restore completed state | |\n| configure_pbtxt_states(\"COMPLETED\") | |\n| print(\"\\n[6/7] Verifying migrated conversation databases...\") | |\n| converted_count = count_converted_chats() | |\n| if converted_count > 0: | |\n| log(f\"Successfully converted {converted_count} conversation history database(s)!\", \"SUCCESS\") | |\n| else: | |\n| if ls_success: | |\n| log(\"No conversations found, or the IDE will process them upon restart.\", \"INFO\") | |\n| else: | |\n| log(\"Conversation database conversion was not completed. They will merge on next IDE launch.\", \"WARNING\") | |\n| print(\"\\n[7/7] Launching fresh Antigravity IDE instance...\") | |\n| reopen_ide() | |\n| print(f\"\"\" | |\n| ============================================================ | |\n| MIGRATION COMPLETE! | |\n| ============================================================ | |\n| All items successfully merged into Antigravity IDE. | |\n| Logs are saved to: {LOG_FILE} | |\n| You're ready to pick up exactly where you left off! | |\n| ============================================================ | |\n| \"\"\") | |\n| if __name__ == \"__main__\": | |\n| main() |", "url": "https://wpnews.pro/news/migrate-antigravity-py", "canonical_source": "https://gist.github.com/antimirov/ee2fe0dbee8c5a5f4b191122669eff5d", "published_at": "2026-05-21 19:53:09+00:00", "updated_at": "2026-06-13 06:18:00.517405+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Antigravity", "Antigravity IDE", "Gemini"], "alternates": {"html": "https://wpnews.pro/news/migrate-antigravity-py", "markdown": "https://wpnews.pro/news/migrate-antigravity-py.md", "text": "https://wpnews.pro/news/migrate-antigravity-py.txt", "jsonld": "https://wpnews.pro/news/migrate-antigravity-py.jsonld"}}