
Hay un momento muy específico que me hizo empezar este proyecto.
Estaba en medio de un refactor grande. Días de contexto acumulado: decisiones, callejones sin salida, el "no, eso ya lo probamos y falló por X". Todo eso vive en ~/.claude/projects/, en archivos JSONL que Claude Code lee para recordar de qué estábamos hablando.
Entonces me senté en la otra máquina.
Contexto cero. Como si nunca hubiéramos hablado.
rsync no lo arreglaMi primer instinto fue el de cualquiera: es una carpeta, la copio y ya.
No funciona. Y la razón es más interesante de lo que parece.
Claude Code guarda cada proyecto en una carpeta cuyo nombre es la ruta absoluta del proyecto, con todos los caracteres no alfanuméricos reemplazados por guiones:
/home/alice/work/myapp → -home-alice-work-myapp \
/Users/alice/myapp → -Users-alice-myapp
Ese encoding es destructivo: my_app, my-app y my.app colapsan todos a my-app. No se puede revertir.
Y no es solo el nombre de la carpeta. Dentro de cada JSONL, cada línea lleva la ruta absoluta incrustada:
{"cwd":"/home/alice/work/myapp","type":"user", ...} \
{"toolUseResult":{"filePath":"/home/alice/work/myapp/src/index.ts"}, ...}
Copias la carpeta a otra máquina donde el proyecto vive en /Users/alice/myapp y Claude Code no encuentra nada. Las rutas no coinciden. El historial existe pero es invisible.
La solución obvia —buscar y reemplazar la ruta vieja por la nueva en todo el archivo— es exactamente la solución equivocada, y me tomó un rato entender por qué.
Un JSONL de sesión contiene dos tipos de rutas mezcladas:
Rutas estructurales — el cwd de la sesión, el filePath que Claude leyó. Estas deben reescribirse, o Claude Code no resuelve nada.
Rutas históricas — la salida de un ls que corriste hace dos días, un stack trace que pegaste, el texto de tu propia conversación.
Si reescribes las segundas, estás falsificando el historial. Ese stdout decía /home/alice/... porque eso fue lo que realmente pasó en esa máquina, ese día. Reescribirlo no arregla nada y corrompe el registro.
Así que la respuesta no es un sed. Son exactamente cuatro campos por línea, y nada más:
cwd
toolUseResult.filePath
toolUseResult.file.filePath
message.content[].input.file_path
Todo lo demás pasa byte por byte, sin tocar.
Hay un detalle que me costó una tarde: si parseas una línea JSON y la vuelves a serializar, aunque no hayas cambiado nada, JSON.stringify normaliza los espacios. El archivo cambia. El checksum cambia. Y de repente tu sync cree que todos los archivos se modificaron, en cada sync, para siempre.
La solución es aburrida y necesaria: llevar un booleano de si la línea realmente cambió. Si no cambió, escribes los bytes originales. No el resultado de re-serializar.
Se llama cortex (cortex-sync en npm). Lo vengo desarrollando desde mayo — hoy son más de cien commits — y hace dos cosas:
Sincroniza tu contexto de Claude Code entre tus propias máquinas. Cifrado antes de salir de tu máquina (AES-256-GCM, la passphrase no se guarda en ningún lado), remapeo automático de rutas al bajar con la cirugía de arriba, y tu propio storage (un repo privado de GitHub, o una carpeta local que ya sincronizas: Dropbox, Syncthing, iCloud).
Comparte el contexto de un proyecto con tu equipo. El CLAUDE.md, los skills, la documentación. Que el dev que entra el lunes tenga el mismo contexto que el que lleva seis meses, sin que nadie se lo explique.
Voy a ser honesto sobre esto, porque creo que es la parte más útil del post.
Construí esto rápido, iterando en público, versión tras versión. Funcionaba, y seguí agregando features. Lo que nunca hice fue sentarme a auditarlo en serio — y un CLI que maneja tokens de GitHub, ejecuta git con URLs que vienen de un archivo de configuración, y escribe archivos en tu disco a partir de un manifiesto remoto, tiene una superficie de ataque que hay que tomarse en serio.
Así que me puse a hacerlo. Y encontré cosas. Una de ellas, seria — no una hipótesis de seguridad, algo que reproduje y verifiqué ejecutando el exploit de verdad.
Prefiero arreglarlas y contar qué encontré, que seguir agregando features encima de una base sin revisar.
El próximo post es esa auditoría completa: qué encontré, cómo lo verifiqué, cómo lo arreglé, y la versión nueva que sale de todo esto.
Si te interesa el problema —o simplemente estás cansado de explicarle a Claude lo mismo tres veces desde tres máquinas distintas— sígueme por acá.
cortex es open source (AGPL-3.0) y ya está en npm desde mayo: npm install -g cortex-sync.