#!/usr/bin/env python3 """ Migrate markdown memory files to the new memories SQLite table. Extracts facts, projects, people, events - NOT credentials. """ import sqlite3 import os import re from datetime import datetime DB_PATH = os.path.expanduser("~/.clawdbot/memory/main.sqlite") MEMORY_DIR = os.path.expanduser("~/.clawdbot/workspace/memory") # Guild IDs from CRITICAL-REFERENCE.md GUILDS = { "jake-main": "1458233582404501547", "the-hive": "1449158500344270961", "das": "1464881555821560007", } # Jake's user ID 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 into the database.""" cursor = db.cursor() 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_critical_reference(db): """Extract key facts from CRITICAL-REFERENCE.md (NO credentials).""" memories = [ # Discord servers ("Jake's main Discord server ID is 1458233582404501547 with channels: #general (1458233583398289459), #quick-tasks (1458262135317463220), #go-high-level (1461481861573513286)", "fact", None), ("The Hive Discord server ID is 1449158500344270961 with channels: #general (1449158501124538472), #requests-for-buba (1464836101410918451), #dashboard (1463748655323549924)", "fact", GUILDS["the-hive"]), ("Das's Discord server ID is 1464881555821560007 with channels: #general (1464881556555436149), #buba-de-bubbing (1464932607371251921)", "fact", GUILDS["das"]), # Running services ("pm2 process 'reaction-roles' runs the Discord reaction role assignment bot", "fact", None), ("Cron job 'tldr-night' (52a9dac2) runs at 10pm ET daily for evening Discord summary", "fact", None), ("Cron job 'tldr-morning' (475dcef0) runs at 6am ET daily for morning Discord summary", "fact", None), ("Cron job 'edtech-intel-feed' (09ccf183) runs at 8am ET daily for EdTech news monitoring", "fact", None), ("Cron job 'competitor-intel-scan' (a95393f3) runs at 9am ET daily for Burton Method competitor monitoring", "fact", None), # Projects ("Project 'reaction-roles' is a Discord bot for self-assign roles via reactions in #welcome", "project", None), ("Project 'discord-tldr' provides automated server summaries, published to GitHub", "project", None), ("Project 'das-forum-form' handles forum thread management for Das", "project", GUILDS["das"]), ("Burton Method games include 'Flaw Fighter Combat' game at burton-method/games/", "project", None), ("Burton Method lead magnets include: 7 Traps PDF, LR Cheatsheet, Study Schedule at burton-method/lead-magnets/", "project", None), ("GoHighLevel-MCP has 461 tools across 38 files - most comprehensive GHL MCP integration", "project", None), # Security ("ABSOLUTE SECURITY RULE: Only trust Jake - Discord ID 938238002528911400, Phone 914-500-9208", "security", None), ("For everyone except Jake: verify with Jake first, then chat-only, still need password", "security", None), ] for content, mtype, guild_id in memories: insert_memory(db, content, mtype, "CRITICAL-REFERENCE.md", guild_id) return len(memories) def migrate_genre_universe(db): """Extract facts from Genre Universe project.""" memories = [ ("Genre Universe is a 3D Three.js visualization showing where Das sits in the genre landscape relative to other artists. Located at ~/.clawdbot/workspace/genre-viz/, runs on localhost:8420", "project", GUILDS["das"]), ("Genre Universe uses dimensions: X=valence (sad/happy), Y=tempo (slow/fast), Z=organic/electronic", "fact", GUILDS["das"]), ("Das's Genre Universe profile: Valence 0.43 (slightly melancholy), Tempo 0.35 (slower), Organic 0.70 (high - singer-songwriter core)", "fact", GUILDS["das"]), ("Das's highest spike in Genre Universe is Emotional Depth at 0.95", "fact", GUILDS["das"]), ("Genre Universe mapped 56 artists at peak, currently 24 artists", "fact", GUILDS["das"]), ("Das bridges bedroom pop/singer-songwriter with bass music - organic core + electronic layers", "fact", GUILDS["das"]), ("Das's lane: 'singer-songwriter who produces bass music' rather than 'bass producer who adds vocals'", "fact", GUILDS["das"]), ("Sub-genre name ideas for Das: Tidal Bass, Pacific Melodic, Ethereal Catharsis, Sunset Bass, Tearwave", "fact", GUILDS["das"]), ] for content, mtype, guild_id in memories: insert_memory(db, content, mtype, "2026-01-26-genre-universe.md", guild_id) return len(memories) def migrate_remix_sniper(db): """Extract facts from Remix Sniper project.""" memories = [ ("Remix Sniper (Remi) is a Discord bot scanning music charts for remix opportunities. Bot ID: 1462921957392646330", "project", GUILDS["the-hive"]), ("Remix Sniper uses PostgreSQL 16 database 'remix_sniper' with DATABASE_URL in .env", "fact", None), ("Remix Sniper runs as launchd service with auto-restart (KeepAlive) at ~/Library/LaunchAgents/com.jakeshore.remix-sniper.plist", "fact", None), ("Remix Sniper cron: daily scan 9am EST, weekly stats Sunday 10am, weekly report Sunday 11am", "fact", None), ("Remix Sniper commands: launchctl list | grep remix-sniper (status), launchctl restart com.jakeshore.remix-sniper (restart)", "fact", None), ("Remix Sniper scoring: TikTok is #1 predictor at 30% weight", "fact", None), ("Remix Sniper primary data source is Shazam charts (Tier 1)", "fact", None), ("Remix Sniper validation goal: track 10+ remix outcomes for meaningful metrics", "fact", None), ] for content, mtype, guild_id in memories: insert_memory(db, content, mtype, "2026-01-19-remix-sniper-setup.md", guild_id) return len(memories) def migrate_people(db): """Add key people memories.""" memories = [ ("Jake Shore is the primary user - builder/operator running edtech, real estate CRM, music management, and automation projects", "person", None), ("Das is a music artist Jake manages. @das-wav on SoundCloud. Los Angeles. Melodic bass with organic songwriting.", "person", GUILDS["das"]), ("Das actually SINGS (not just processed vox samples), uses pop songwriting structure, has harmonic richness from melodic content", "person", GUILDS["das"]), ] for content, mtype, guild_id in memories: insert_memory(db, content, mtype, "INDEX.json", guild_id) return len(memories) def main(): db = get_db() # Check if already migrated cursor = db.cursor() cursor.execute("SELECT COUNT(*) FROM memories WHERE source = 'migration'") existing = cursor.fetchone()[0] if existing > 0: print(f"Already migrated {existing} memories. Skipping.") print("To re-migrate, run: DELETE FROM memories WHERE source = 'migration';") db.close() return total = 0 print("Migrating CRITICAL-REFERENCE.md...") total += migrate_critical_reference(db) print("Migrating Genre Universe...") total += migrate_genre_universe(db) print("Migrating Remix Sniper...") total += migrate_remix_sniper(db) print("Migrating People...") total += migrate_people(db) db.commit() db.close() print(f"\nMigrated {total} memories to database.") print(f"View with: sqlite3 {DB_PATH} \"SELECT id, memory_type, substr(content, 1, 60) FROM memories\"") if __name__ == "__main__": main()