새로운 Antigravity IDE (macOS)로 대화 기록을 마이그레이션하는 방법
요약
Antigravity IDE로 업그레이드 시 발생하는 대화 기록 누락 문제를 해결하기 위한 마이그레이션 가이드입니다. Python 스크립트를 사용하여 SQLite 데이터베이스 내의 하드코딩된 경로를 수정하고 Protobuf 대화 파일을 새 디렉토리로 안전하게 이전하는 방법을 설명합니다.
핵심 포인트
- IDE 전환 시 데이터 경로 변경으로 인한 대화 기록 유실 발생
- Python을 이용한 SQLite 데이터베이스 내 경로 수정 필요
- Protobuf 기반 대화 파일 및 궤적 요약 병합 방법 제공
- 데이터 유실 방지를 위해 스크립트 실행 전 IDE 완전 종료 필수
최근 기존의 Antigravity App에서 새로운 독립형 Antigravity IDE로 업그레이드했다면, 한 가지 당혹스러운 현상을 발견했을 수도 있습니다. 바로 "과거 대화(Past Conversations)" 사이드바가 완전히 비어 있다는 점입니다. Google은 최근 Antigravity IDE를 별도의 애플리케이션으로 분리했습니다. 그 결과, 애플리케이션 데이터 경로가 ~/.gemini/antigravity에서 ~/.gemini/antigravity-ide로 변경되었습니다. IDE는 VS Code의 핵심 아키텍처를 기반으로 구축되었기 때문에, 내부 SQLite 데이터베이스(state.vscdb)에는 이전 디렉토리 구조를 가리키는 하드코딩된 절대 경로가 포함되어 있습니다. 이 가이드에서는 간단한 Python 스크립트를 사용하여 이전 워크스페이스(workspaces), 대화 기록 및 아티팩트(artifact) 링크를 새로운 IDE로 안전하게 마이그레이션하는 방법을 알려드리겠습니다.
문제점
새로운 IDE로 전환하면, 대화 기록 파일(.pb Protobuf 파일로 저장됨)이 이전 디렉토리에 그대로 남게 됩니다. 게다가 파일을 수동으로 복사하더라도, 궤적 요약(trajectory summaries)과 최근에 열었던 워크스페이스가 하드코딩된 레거시 경로를 사용하기 때문에 IDE의 내부 state.vscdb 데이터베이스가 이를 인식하지 못합니다.
해결책: Python 마이그레이션 스크립트
이를 해결하기 위해 다음 작업이 필요합니다:
- 최신 .pb 대화 파일을 새로운 IDE 폴더로 복사합니다.
- 전역(global) 및 워크스페이스 저장소 모두에 있는 VS Code SQLite 데이터베이스(
state.vscdb)에 연결합니다. - Base64로 인코딩된 Protobuf 궤적 요약을 병합하여 이전 기록과 새 기록이 모두 결합되도록 합니다.
- 이중 접미사(double-suffixes)가 발생하지 않도록 레거시 경로(
antigravity→antigravity-ide)를 지능적으로 찾아 교체합니다.
⚠️ 주의 사항: IDE는 종료될 때 데이터베이스 상태를 디스크에 플러시(flush)합니다. 이 스크립트를 실행하기 전에 반드시 IDE를 완전히 종료(Cmd + Q)해야 합니다. 만약 IDE가 열려 있다면, 종료하는 순간 데이터베이스를 덮어씌워 마이그레이션 내용을 지워버릴 것입니다!
1단계: 마이그레이션 스크립트 저장
migrate_antigravity.py라는 이름의 새 파일을 생성하고 다음 Python 코드를 붙여넣으세요:
import os
import sqlite3
import shutil
import base64
import json
import re
def log ( msg ):
print ( f " [*] { msg } " )
def replace_paths ( val ):
if not isinstance ( val , str ):
return val
# 중복 접미사(예: antigravity-ide-ide)를 생성하지 않고 레거시 경로를 지능적으로 교체합니다.
val = re . sub ( r ' /Users/([^/]+)/\.gemini/antigravity(?!-ide) ' , r ' /Users/\1/.gemini/antigravity-ide ' , val )
val = re . sub ( r ' Application Support/Antigravity(?! IDE) ' , r ' Application Support/Antigravity IDE ' , val )
return val
def parse_summaries ( data ):
# 궤적 요약(trajectory summaries)을 위해 반복되는 protobuf 메시지를 파싱합니다.
summaries = []
i = 0
while i < len ( data ):
if data [ i ] != 0x0a :
i += 1
continue
i += 1
length , shift = 0 , 0
while i < len ( data ):
b = data [ i ]
i += 1
length |= ( b & 0x7f ) << shift
if not ( b & 0x80 ):
break
shift += 7
summaries . append ( data [ i : i + length ])
i += length
return summaries
def encode_varint ( value ):
out = []
while value >= 0x80 :
out . append (( value & 0x7f ) | 0x80 )
value >>= 7
out . append ( value )
return bytes ( out )
def get_trajectory_id ( msg_bytes ):
if len ( msg_bytes ) >= 38 and msg_bytes [ 0 ] == 0x0a and msg_bytes [ 1 ] == 0x24 :
return msg_bytes [ 2 : 38 ]. decode ( ' utf-8 ' , errors = ' ignore ' )
return None
def merge_trajectory_summaries ( old_val_b64 , new_val_b64 ):
old_data = base64 . b64decode ( old_val_b64 ) if old_val_b64 else b ""
new_data = base64 . b64decode ( new_val_b64 ) if new_val_b64 else b ""
merged_dict = {}
for s in parse_summaries ( old_data ):
if tid : =
get_trajectory_id ( s ):
merged_dict [ tid ] = s
for s in parse_summaries ( new_data ):
if tid : =
get_trajectory_id ( s ):
merged_dict [ tid ] = s
out = bytearray ()
for s in merged_dict . values ():
out . append ( 0x0a )
out . extend ( encode_varint ( len ( s )))
out . extend ( s )
return base64 .
b64encode(out).decode('ascii')
def main():
home = os.path.expanduser("~")
old_global_db = os.path.join(home, "Library/Application Support/Antigravity/User/globalStorage/state.vscdb")
new_global_db = os.path.join(home, "Library/Application Support/Antigravity IDE/User/globalStorage/state.vscdb")
old_ws_dir = os.path.join(home, "Library/Application Support/Antigravity/User/workspaceStorage")
new_ws_dir = os.path.join(home, "Library/Application Support/Antigravity IDE/User/workspaceStorage")
# 1. Global Storage Migration
log("Migrating Global Storage...")
if os.path.exists(new_global_db):
shutil.copy2(new_global_db, new_global_db + " .backup")
old_g_conn = sqlite3.connect(old_global_db)
new_g_conn = sqlite3.connect(new_global_db)
old_items = {row[0]: row[1] for row in old_g_conn.execute(" SELECT key, value FROM ItemTable WHERE key LIKE 'antigravity% ' OR key LIKE 'google.antigravity% ' OR key = 'history.recentlyOpenedPathsList'")}
new_items = {row[0]: row[1] for row in new_g_conn.execute(" SELECT key, value FROM ItemTable ")}
for key, old_val in old_items:
items():
if "notification" in key: continue
if key == "antigravityUnifiedStateSync.trajectorySummaries":
merged = merge_trajectory_summaries(old_val, new_items.get(key))
new_g_conn.execute(" INSERT OR REPLACE INTO ItemTable (key, value) VALUES (?, ?) ", (key, merged))
elif key == "history.recentlyOpenedPathsList":
# Simple JSON merge (implementation omitted for brevity, fallback to replace_paths)
new_g_conn.execute(" INSERT OR REPLACE INTO ItemTable (key, value) VALUES (?, ?) ", (key, replace_paths(old_val)))
else:
if key not in new_items or "Preferences" in key:
new_g_conn.execute(" INSERT OR REPLACE INTO ItemTable (key, value) VALUES (?, ?) ", (key, replace_paths(old_val)))
new_g_conn.commit()
old_g_conn.close(); new_g_conn.close()
# 2.
Workspace Storage Migration 로그 ("Migrating Workspace Storage...") os . makedirs ( new_ws_dir , exist_ok = True ) if os . path . exists ( old_ws_dir ): for folder in os . listdir ( old_ws_dir ): old_db = os . path . join ( old_ws_dir , folder , " state.vscdb " ) if not os . path . exists ( old_db ): continue new_folder_path = os . path . join ( new_ws_dir , folder ) os . makedirs ( new_folder_path , exist_ok = True ) new_db = os . path . join ( new_folder_path , " state.vscdb " ) new_conn = sqlite3 . connect ( new_db ) new_conn . execute ( " CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value TEXT) " ) for row in sqlite3 . connect ( old_db ). execute ( " SELECT key, value FROM ItemTable " ). fetchall (): new_conn . execute ( " INSERT OR REPLACE INTO ItemTable (key, value) VALUES (?, ?) " , ( row [ 0 ], replace_paths ( row [ 1 ]))) new_conn . commit () new_conn . close () # 3. Copy Conversation files 로그 ("Copying Conversation Protobufs...") old_conv_dir = os . path . join ( home , " .gemini/antigravity/conversations " ) new_conv_dir = os . path . join ( home , " .gemini/antigravity-ide/conversations " ) os . makedirs ( new_conv_dir , exist_ok = True ) if os . path . exists ( old_conv_dir ): for filename in os . listdir ( old_conv_dir ): if filename . endswith ( " .pb " ): shutil . copy2 ( os . path . join ( old_conv_dir , filename ), os . path . join ( new_conv_dir , filename )) log (" Migration completed successfully! " ) if __name__ == " __main__ " : main ()
**Step 2: 마이그레이션 실행하기**
Antigravity IDE를 완전히 종료하세요 ( Cmd + Q ). 이 과정은 매우 중요합니다. macOS Terminal을 열고 다음 스크립트를 실행하세요:
`python3 migrate_antigravity.py`
[*]
'Migration completed successfully!' 메시지를 찾으세요.
Antigravity IDE를 다시 엽니다.
그게 전부입니다! 이전에 저장했던 모든 아티팩트, 활성 워크스페이스, 그리고 과거 대화 기록이 새로운 IDE 앱의 사이드바에 완벽하게 복원될 것입니다. 즐거운 코딩 되세요!
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기