chasm-cli 1.5.4

Universal chat session manager - harvest, merge, and analyze AI chat history from VS Code, Cursor, and other editors
Documentation
"""
Dump the session index and model cache for both workspaces,
and check what VS Code actually sees at startup.
"""
import sqlite3
import json
import os

WS_STORAGE = r"C:\Users\adamm\AppData\Roaming\Code\User\workspaceStorage"
BROKEN_HASH = "5ec71800c69c79b96b06a37e38537907"
WORKING_HASH = "82cdabb21413f2ff42168423e82c8bdf"

def get_db_value(ws_hash, key):
    db_path = os.path.join(WS_STORAGE, ws_hash, "state.vscdb")
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute("SELECT value FROM ItemTable WHERE key = ?", (key,))
    row = cursor.fetchone()
    conn.close()
    return row[0] if row else None

def get_all_chat_keys(ws_hash):
    db_path = os.path.join(WS_STORAGE, ws_hash, "state.vscdb")
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute("SELECT key, length(value) FROM ItemTable WHERE key LIKE '%chat%' OR key LIKE '%session%' OR key LIKE '%interactive%' OR key LIKE '%agent%'")
    rows = cursor.fetchall()
    conn.close()
    return rows

for ws_hash, label in [(BROKEN_HASH, "Agentic (BROKEN)"), (WORKING_HASH, "chasm (WORKING)")]:
    print(f"\n{'='*80}")
    print(f"{label}")
    print(f"{'='*80}")
    
    # All chat keys
    print("\nAll chat-related DB keys:")
    for key, size in get_all_chat_keys(ws_hash):
        print(f"  {key}: {size} bytes")
    
    # Session index
    raw = get_db_value(ws_hash, "chat.ChatSessionStore.index")
    if raw:
        data = json.loads(raw)
        print(f"\nchat.ChatSessionStore.index ({len(raw)} bytes):")
        print(json.dumps(data, indent=2)[:3000])
    
    # Model cache (just metadata about cached sessions)
    raw = get_db_value(ws_hash, "agentSessions.model.cache")
    if raw:
        data = json.loads(raw)
        print(f"\nagentSessions.model.cache ({len(raw)} bytes):")
        print(json.dumps(data, indent=2)[:3000])
    
    # memento/interactive-session (input history)
    raw = get_db_value(ws_hash, "memento/interactive-session")
    if raw:
        data = json.loads(raw)
        print(f"\nmemento/interactive-session ({len(raw)} bytes):")
        # Truncate messages for readability
        if isinstance(data, dict) and "history" in data:
            hist = data["history"]
            for provider, entries in hist.items():
                if isinstance(entries, list):
                    print(f"  {provider}: {len(entries)} history entries")
                    for i, entry in enumerate(entries):
                        if isinstance(entry, dict):
                            msg = entry.get("inputText", "")
                            msg_preview = msg[:80] if isinstance(msg, str) else str(type(msg))
                            selections = entry.get("selections", [])
                            attachments = entry.get("attachments", [])
                            print(f"    [{i}] msg={msg_preview}...")
                            print(f"         selections={len(selections)}, attachments={len(attachments)}")
                            if selections:
                                for s in selections[:2]:
                                    print(f"         sel: {json.dumps(s)}")
    
    # Check all session JSONL files and verify they match the index
    sessions_dir = os.path.join(WS_STORAGE, ws_hash, "chatSessions")
    if os.path.isdir(sessions_dir):
        files = [f for f in os.listdir(sessions_dir) if f.endswith('.jsonl')]
        print(f"\nchatSessions/ files: {len(files)}")
        for f in sorted(files):
            fpath = os.path.join(sessions_dir, f)
            size = os.path.getsize(fpath)
            sid = f.replace('.jsonl', '')
            
            # Check if in index
            index_raw = get_db_value(ws_hash, "chat.ChatSessionStore.index")
            if index_raw:
                index_data = json.loads(index_raw)
                in_index = any(e.get("sessionId") == sid for e in index_data) if isinstance(index_data, list) else False
            else:
                in_index = False
            
            # Read the file and check structure
            with open(fpath, 'r', encoding='utf-8') as fp:
                content = fp.read()
            
            try:
                decoder = json.JSONDecoder()
                obj, _ = decoder.raw_decode(content.strip())
                kind = obj.get("kind")
                v = obj.get("v", {})
                reqs = v.get("requests", []) if isinstance(v, dict) else []
                session_id_in_file = v.get("sessionId", "missing")
                version = v.get("version", "missing")
                creation_date = v.get("creationDate", "missing")
                custom_title = v.get("customTitle", "missing")
                
                # Check message types
                msg_types = []
                for req in reqs:
                    msg = req.get("message")
                    if isinstance(msg, str):
                        msg_types.append("string")
                    elif isinstance(msg, dict):
                        msg_types.append("dict")
                    else:
                        msg_types.append(f"other({type(msg).__name__})")
                
                print(f"  {sid}: {size}b, kind={kind}, v={version}, reqs={len(reqs)}, inIndex={in_index}")
                print(f"    title='{custom_title}', created={creation_date}")
                if msg_types:
                    print(f"    msg_types={msg_types[:5]}{'...' if len(msg_types) > 5 else ''}")
            except Exception as e:
                print(f"  {sid}: {size}b, PARSE ERROR: {e}")