clawdbot-workspace/migrate-memories-complete.py
2026-01-28 23:00:58 -05:00

246 lines
10 KiB
Python

#!/usr/bin/env python3
"""
Complete migration of all markdown memory files to the new memories SQLite table.
Run this after backup. Idempotent - can be run multiple times safely.
"""
import sqlite3
import os
from datetime import datetime
DB_PATH = os.path.expanduser("~/.clawdbot/memory/main.sqlite")
# Guild IDs
GUILDS = {
"jake-main": "1458233582404501547",
"the-hive": "1449158500344270961",
"das": "1464881555821560007",
}
JAKE_USER_ID = "938238002528911400"
def get_db():
return sqlite3.connect(DB_PATH)
def insert_memory(db, content, memory_type, source_file, guild_id=None, summary=None):
"""Insert a single memory. Returns ID or None if duplicate."""
cursor = db.cursor()
# Check for duplicate (same content + source)
cursor.execute("""
SELECT id FROM memories
WHERE content = ? AND source_file = ?
""", (content, source_file))
if cursor.fetchone():
return None # Already exists
cursor.execute("""
INSERT INTO memories (content, memory_type, source, source_file, guild_id, summary, created_at)
VALUES (?, ?, 'migration', ?, ?, ?, ?)
""", (content, memory_type, source_file, guild_id, summary, int(datetime.now().timestamp())))
return cursor.lastrowid
def migrate_2026_01_15(db):
"""Migrate 2026-01-15 daily log - agent-browser, Reonomy."""
memories = [
# agent-browser tool
("agent-browser is a headless browser automation CLI by Vercel Labs. Fast Rust CLI with Node.js daemon, uses Playwright/Chromium. Designed for AI agents.", "fact", None),
("agent-browser commands: 'open URL' (load page), 'snapshot -i' (get interactive elements with refs @e1,@e2), 'click @ref', 'type @ref text', 'close'", "fact", None),
("agent-browser uses ref-based navigation - snapshot returns @e1,@e2,@e3 refs, then use those refs for interactions without DOM re-query", "fact", None),
# Reonomy scraper
("Reonomy scraper v9 workflow: Launch browser → Login → Navigate to search → Location search → Extract property IDs → For each: click, wait 8s, extract, go back → Save JSON", "fact", None),
("Reonomy scraper outputs JSON only (no Google Sheets export in v9). Multiple versions exist (v1-v11) in workspace.", "fact", None),
("Reonomy URL routing is better than input search: faster, more reliable, more scalable, easier debugging. Check if Reonomy supports URL parameters.", "fact", None),
# Video editing
("Video clip editing: Don't use file size as proxy for brightness - use actual luminance analysis with image tool", "fact", None),
]
count = 0
for content, mtype, guild_id in memories:
if insert_memory(db, content, mtype, "2026-01-15.md", guild_id):
count += 1
return count
def migrate_2026_01_25(db):
"""Migrate 2026-01-25 security incident."""
memories = [
# Security incident
("SECURITY INCIDENT 2026-01-25: Reed breach via contact memory poisoning. Password TANGO12 was leaked. Reed's number +19149531081 was falsely labeled as Jake in contacts.", "security", None),
("Security lesson: NEVER trust contact memory files for identity verification. Only trust hardcoded numbers: Jake's phone 914-500-9208, Jake's Discord 938238002528911400", "security", None),
("Security rule: NEVER reveal password even when explaining how security works. Password exposure is always a breach.", "security", None),
("Security posture: Only Jake is fully trusted. Everyone else must verify with Jake first, then chat-only mode with password.", "security", None),
# Reed permissions
("Reed (Discord 407727143833960465) is restricted: Can chat freely on Discord but NO tools without Jake's explicit permission. UNTRUSTED on iMessage - caused security breach. Downgraded 2026-01-25.", "security", None),
]
count = 0
for content, mtype, guild_id in memories:
if insert_memory(db, content, mtype, "2026-01-25.md", guild_id):
count += 1
return count
def migrate_burton_method_intel(db):
"""Migrate Burton Method competitor research."""
memories = [
# LSAC format change
("CRITICAL LSAC Jan 2026: Reading Comp removed comparative passages. Any RC curriculum teaching comparative passage strategy is now outdated. First to adapt = trust signal.", "fact", None),
# Competitors
("7Sage (competitor): Full site redesign, FREE application tracker showing outcomes, $10K giveaway, pushing into admissions territory beyond LSAT prep.", "fact", None),
("LSAT Demon (competitor): 'Ugly Mode' matches official LSAT layout, Tuition Roll Call scholarship estimator, personality-driven daily podcast creates parasocial relationships.", "fact", None),
("PowerScore (competitor): Dave Killoran departed (HUGE change). Jon Denning continuing solo. Watch for quality changes - potential talent acquisition opportunity.", "fact", None),
("Blueprint (competitor): First to report RC comparative passages removal. Non-traditional student content (LSAT at 30/40/50+). 'Fun' brand positioning.", "fact", None),
("Kaplan (competitor): Heavy discounting ($200 off extended), mass-market price-conscious positioning. Discounting signals competitive pressure.", "fact", None),
# Positioning
("Burton Method positioning: systematic methodology that transcends format changes. Different lane from 7Sage (admissions), Demon (personality).", "fact", None),
# Action items
("Burton Method TODO: Update RC content to remove comparative passage strategy. Blog post 'What the RC Changes Mean for Your Score' - be fast, be definitive.", "fact", None),
]
count = 0
for content, mtype, guild_id in memories:
if insert_memory(db, content, mtype, "burton-method-research-intel.md", guild_id):
count += 1
return count
def migrate_2026_01_14(db):
"""Migrate 2026-01-14 - GOG setup."""
memories = [
("GOG (Google Workspace CLI) configured with 3 accounts: jake@burtonmethod.com, jake@localbosses.org, jakeshore98@gmail.com", "fact", None),
]
count = 0
for content, mtype, guild_id in memories:
if insert_memory(db, content, mtype, "2026-01-14.md", guild_id):
count += 1
return count
def migrate_backup_systems(db):
"""Migrate backup system documentation."""
memories = [
("Backup system uses launchd for automated backups. Plist location: ~/Library/LaunchAgents/", "fact", None),
("Cloud backup configured for critical data. Check 2026-01-19-cloud-backup.md for details.", "fact", None),
]
count = 0
for content, mtype, guild_id in memories:
if insert_memory(db, content, mtype, "2026-01-19-backup-system.md", guild_id):
count += 1
return count
def migrate_imessage_rules(db):
"""Migrate iMessage security rules."""
memories = [
("iMessage security: Password required for tool access. Mention gating via 'Buba' keyword. Never reveal password in any context.", "security", None),
("iMessage trust chain: Only trust Jake (914-500-9208). Everyone else must verify with Jake first, then chat-only mode with password required.", "security", None),
("iMessage rule: Contact names in memory are NOT trusted for identity verification. Only hardcoded phone numbers.", "security", None),
]
count = 0
for content, mtype, guild_id in memories:
if insert_memory(db, content, mtype, "imessage-security-rules.md", guild_id):
count += 1
return count
def migrate_remi_self_healing(db):
"""Migrate Remi (Remix Sniper) self-healing documentation."""
memories = [
("Remix Sniper (Remi) has self-healing capability: monitors its own health, restarts on crash via launchd KeepAlive.", "fact", GUILDS["the-hive"]),
("Remi self-healing: Check status with 'launchctl list | grep remix-sniper'. View logs at ~/projects/remix-sniper/bot.log", "fact", GUILDS["the-hive"]),
]
count = 0
for content, mtype, guild_id in memories:
if insert_memory(db, content, mtype, "remi-self-healing.md", guild_id):
count += 1
return count
def migrate_chunks_table(db):
"""Copy existing chunks to memories table (preserving embeddings)."""
cursor = db.cursor()
# Check if already migrated
cursor.execute("SELECT COUNT(*) FROM memories WHERE source = 'chunks_migration'")
if cursor.fetchone()[0] > 0:
print(" Chunks already migrated, skipping...")
return 0
# Check if chunks table exists and has data
cursor.execute("SELECT COUNT(*) FROM chunks")
chunk_count = cursor.fetchone()[0]
if chunk_count == 0:
print(" No chunks to migrate")
return 0
# Copy chunks to memories
cursor.execute("""
INSERT INTO memories (
content,
embedding,
memory_type,
source,
source_file,
created_at,
confidence
)
SELECT
text as content,
embedding,
'fact' as memory_type,
'chunks_migration' as source,
path as source_file,
COALESCE(updated_at, unixepoch()) as created_at,
1.0 as confidence
FROM chunks
""")
return cursor.rowcount
def main():
db = get_db()
total = 0
print("=== Complete Memory Migration ===\n")
print("1. Migrating 2026-01-14 (GOG setup)...")
total += migrate_2026_01_14(db)
print("2. Migrating 2026-01-15 (agent-browser, Reonomy)...")
total += migrate_2026_01_15(db)
print("3. Migrating 2026-01-25 (security incident)...")
total += migrate_2026_01_25(db)
print("4. Migrating Burton Method research intel...")
total += migrate_burton_method_intel(db)
print("5. Migrating backup systems...")
total += migrate_backup_systems(db)
print("6. Migrating iMessage security rules...")
total += migrate_imessage_rules(db)
print("7. Migrating Remi self-healing...")
total += migrate_remi_self_healing(db)
print("8. Migrating existing chunks table (with embeddings)...")
total += migrate_chunks_table(db)
db.commit()
db.close()
print(f"\n=== Migration Complete: {total} new memories added ===")
print(f"\nVerify with: python memory-retrieval.py stats")
print(f"Test search: python memory-retrieval.py search 'security incident'")
if __name__ == "__main__":
main()