Help us improve
Share bugs, ideas, or general feedback.
From cortex
Imports memories from claude-mem, Claude Desktop, ChatGPT, Gemini, Cursor, and Claude Code JSONL into Cortex. Detects sources and runs migration autonomously.
npx claudepluginhub cdeust/cortex --plugin cortexHow this skill is triggered — by the user, by Claude, or both
Slash command
/cortex:cortex-importThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Detect available memory sources and import them into Cortex. Run fully autonomously — detect, import, consolidate.
Exports AI-stored memories, context, preferences, and instructions using a copy-paste prompt for migration to platforms like Claude, ChatGPT, or Gemini. Use for switching services, backups, audits, or resets.
Cross-host durable memory for AI agents using the ling-mem CLI. Maintains a three-tier model of who the user is across sessions and hosts (Claude Code, Codex, OpenClaw).
Manages the total-recall memory system: captures, stores, retrieves, and forgets memories across tiers. Activates on /total-recall:commands or when users ask about memory, knowledge bases, or eval.
Share bugs, ideas, or general feedback.
Detect available memory sources and import them into Cortex. Run fully autonomously — detect, import, consolidate.
Run this bash command to detect all sources:
echo "=== Memory Sources ==="
# 1. Claude Code JSONL
CC=$(find ~/.claude/projects -name "*.jsonl" 2>/dev/null | wc -l | tr -d ' ')
echo "Claude Code JSONL: $CC files"
# 2. Claude Desktop sessions
CD=$(find ~/Library/Application\ Support/Claude/claude-code-sessions -name "*.json" 2>/dev/null | wc -l | tr -d ' ')
echo "Claude Desktop: $CD session files"
# 3. claude-mem
if [ -f ~/.claude-mem/claude-mem.db ]; then
CM=$(sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations" 2>/dev/null || echo "0")
echo "claude-mem: $CM observations"
else
echo "claude-mem: not found"
fi
# 4. ChatGPT — desktop app stores binary, need web export
CHATGPT=$(find ~/Downloads -name "conversations.json" -maxdepth 3 2>/dev/null | head -1)
if [ -n "$CHATGPT" ]; then
echo "ChatGPT export: $CHATGPT"
else
echo "ChatGPT: no export (get from chatgpt.com → Settings → Data controls → Export)"
fi
# 5. Gemini Takeout
GEMINI=$(find ~/Downloads -path "*Gemini*" -name "*.json" -maxdepth 5 2>/dev/null | head -1)
if [ -n "$GEMINI" ]; then
echo "Gemini export: $GEMINI"
else
echo "Gemini: no export (get from takeout.google.com → select Gemini Apps)"
fi
# 6. Cursor
if [ -d ~/.cursor ]; then
CU=$(find ~/.cursor -name "*.jsonl" 2>/dev/null | wc -l | tr -d ' ')
echo "Cursor: $CU files"
else
echo "Cursor: not installed"
fi
Report findings, then proceed with each detected source.
If Claude Code JSONL files exist (always the case):
cortex:backfill_memories({"max_files": 500, "min_importance": 0.3, "force_reprocess": false})
If ~/.claude-mem/claude-mem.db exists, run:
DEPS_DIR="$HOME/.claude/plugins/data/cortex-cortex-plugins/deps"
PYTHONPATH="${CLAUDE_PLUGIN_ROOT:-/Users/cdeust/.claude/plugins/marketplaces/cortex-plugins}:$DEPS_DIR" \
DATABASE_URL="${DATABASE_URL:-postgresql://localhost:5432/cortex}" \
python3 -c "
import sqlite3, json, asyncio, sys, os
db_path = os.path.expanduser('~/.claude-mem/claude-mem.db')
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
rows = conn.execute('SELECT type, title, narrative, facts, concepts, created_at_epoch, project FROM observations ORDER BY created_at_epoch').fetchall()
print(f'Found {len(rows)} claude-mem observations')
from mcp_server.handlers.remember import handler as remember_handler
from datetime import datetime, timezone
imported = 0
for row in rows:
parts = []
if row['title']: parts.append(row['title'])
if row['narrative']: parts.append(row['narrative'])
if row['facts']:
try:
for f in json.loads(row['facts']): parts.append(str(f))
except: pass
content = '\n'.join(parts)
if len(content) < 20: continue
tags = ['imported', 'claude-mem']
if row['type']: tags.append(row['type'])
if row['concepts']:
try: tags.extend(json.loads(row['concepts'])[:5])
except: pass
created_at = None
if row['created_at_epoch']:
try: created_at = datetime.fromtimestamp(row['created_at_epoch'], tz=timezone.utc).isoformat()
except: pass
result = asyncio.run(remember_handler({'content': content, 'tags': tags, 'domain': row['project'] or '', 'source': 'claude-mem', 'force': True, 'created_at': created_at}))
if result.get('stored'): imported += 1
print(f'Imported {imported} memories from claude-mem')
conn.close()
"
If a conversations.json file was found in Downloads, or the user provides one:
DEPS_DIR="$HOME/.claude/plugins/data/cortex-cortex-plugins/deps"
PYTHONPATH="${CLAUDE_PLUGIN_ROOT:-/Users/cdeust/.claude/plugins/marketplaces/cortex-plugins}:$DEPS_DIR" \
DATABASE_URL="${DATABASE_URL:-postgresql://localhost:5432/cortex}" \
python3 -c "
import json, asyncio, sys, os
path = 'REPLACE_WITH_PATH_TO_CONVERSATIONS_JSON'
with open(path) as f: data = json.load(f)
from mcp_server.handlers.remember import handler as remember_handler
from datetime import datetime, timezone
imported = 0
for conv in data:
title = conv.get('title', '')
for nid, node in (conv.get('mapping') or {}).items():
msg = node.get('message') or {}
if not msg: continue
role = (msg.get('author') or {}).get('role', '')
if role not in ('user', 'assistant'): continue
parts = msg.get('content', {}).get('parts', [])
text = '\n'.join(str(p) for p in parts if isinstance(p, str))
if len(text) < 30: continue
created_at = None
ct = msg.get('create_time')
if ct:
try: created_at = datetime.fromtimestamp(ct, tz=timezone.utc).isoformat()
except: pass
result = asyncio.run(remember_handler({'content': text[:4000], 'tags': ['imported', 'chatgpt', role], 'domain': title[:50] or 'chatgpt', 'source': 'chatgpt', 'force': False, 'created_at': created_at}))
if result.get('stored'): imported += 1
print(f'Imported {imported} memories from ChatGPT')
"
If no export file exists, tell the user:
ChatGPT desktop app stores conversations in binary format — not readable. To import, export from the web:
- Go to chatgpt.com → Settings → Data controls → Export data
- Wait for the email with the download link
- Unzip to ~/Downloads/
- Run
/cortex-importagain
If Gemini Takeout JSON files found, scan the Gemini Apps/ folder for conversation JSON:
DEPS_DIR="$HOME/.claude/plugins/data/cortex-cortex-plugins/deps"
PYTHONPATH="${CLAUDE_PLUGIN_ROOT:-/Users/cdeust/.claude/plugins/marketplaces/cortex-plugins}:$DEPS_DIR" \
DATABASE_URL="${DATABASE_URL:-postgresql://localhost:5432/cortex}" \
python3 -c "
import json, asyncio, sys, os, glob
gemini_dir = 'REPLACE_WITH_PATH_TO_GEMINI_TAKEOUT'
files = glob.glob(os.path.join(gemini_dir, '**/*.json'), recursive=True)
from mcp_server.handlers.remember import handler as remember_handler
imported = 0
for fp in files:
try:
with open(fp) as f: data = json.load(f)
except: continue
if not isinstance(data, list): data = [data]
for item in data:
parts = item.get('parts', [])
for part in parts:
text = part.get('text', '')
if len(text) < 30: continue
result = asyncio.run(remember_handler({'content': text[:4000], 'tags': ['imported', 'gemini'], 'domain': 'gemini', 'source': 'gemini', 'force': False}))
if result.get('stored'): imported += 1
print(f'Imported {imported} memories from Gemini')
"
If no Gemini export exists:
To import Gemini conversations:
- Go to takeout.google.com
- Deselect all, then select Gemini Apps
- Download and unzip to ~/Downloads/
- Run
/cortex-importagain
After all imports:
cortex:consolidate({})
cortex:memory_stats({})
Print one summary block with all results.