TL;DR El
@pega el fichero entero, y además elCLAUDE.mdque haya por encima en su árbol. Escríbelo una vez, en el turno en el que el fichero tiene que entrar, y a partir de ahí nómbralo con palabras. Repetir la mención me costó 4117 tokens nuevos, frente a 185 del mismo turno sin ella.
El @ parece un puntero. Escribes @src/auth/session.ts, y suena a "mira este fichero", como quien señala con el dedo.
No señala. Pega. Y lo que casi no se ve es que vuelve a pegar cada vez que lo escribes, porque el modelo no recuerda que ya se lo diste en el turno anterior.
Lo medí en una sesión limpia, leyendo los registros de la propia sesión.
Resultado:
turno lo que escribí tokens nuevos historial releído
1 @gsc-report.md @gsc-report.md 15139 14112
2 @gsc-report.md (ya estaba dentro) 4117 29251
3 una frase igual de larga, sin @ 185 33368
4 @voice-profile.md 4188 33553
El turno 3 es el control: la misma longitud de frase, sin mención. 185 tokens. El turno 2, que solo añadía un @ a un fichero de 2 KB que ya llevaba dentro desde el turno 1, costó 4117. Veintidós veces más.
Tres cosas que cambian cómo lo usas
1. Dentro del mismo mensaje sí deduplica. En el turno 1 escribí el mismo fichero dos veces y solo se adjuntó una copia. Así que la versión que corre por ahí, la de "mencionarlo tres veces son tres copias", no es cierta dentro de un mensaje.
2. Entre turnos no deduplica. Ahí sí. El turno 2 volvió a adjuntar el fichero completo aunque estaba en el historial desde el turno 1. Lo que se paga no es la mención repetida, es el turno repetido.
3. Y no viene solo. Cada @ arrastra también el CLAUDE.md del árbol de ese fichero, como un adjunto aparte. Pedí harness/commands/gsc-report.md, que son 2235 bytes, y con él entró harness/CLAUDE.md, que son 8383. El 79% de lo que se adjuntó no era el fichero que pedí. El turno 4 lo confirma por el otro lado: mencioné un fichero sin ningún CLAUDE.md por encima y ese adjunto extra no apareció.
La doc lo dice de pasada, sin números: las referencias @ añaden al contexto el CLAUDE.md del directorio del fichero y de sus directorios padre. En mi caso subió un nivel, de commands/ a harness/.
Míralo en tu propia sesión
Los adjuntos de cada turno están en el .jsonl de la sesión, con nombre y tipo:
jq -r 'select(.type=="attachment") | .attachment
| select(.type=="file" or .type=="nested_memory")
| "\(.type)\t\(.filename // .path)"' \
~/.claude/projects/<tu-proyecto>/<sesion>.jsonl
Un file por fichero mencionado y un nested_memory por cada CLAUDE.md que se vino de propina. Si el mismo nombre sale tres veces, es que lo has mencionado en tres turnos.
Y el precio de cada turno, en el mismo fichero:
jq -s '[.[] | select(.type=="assistant") | .message | select(.usage)]
| unique_by(.id) | to_entries
| map({turno: (.key+1),
nuevo: .value.usage.cache_creation_input_tokens,
historial: .value.usage.cache_read_input_tokens})' \
~/.claude/projects/<tu-proyecto>/<sesion>.jsonl
El unique_by(.id) no es decorativo: la misma respuesta aparece varias veces en el fichero y sin él cuentas el doble.
Lo que de verdad duele es que se queda
Mira otra vez la columna de la derecha. El turno 3 no mencionó nada y aun así releyó 33368 tokens, que son los 29251 del turno anterior más los 4117 que había añadido el turno 2.
Una copia no se paga una vez. Se queda dentro y se relee en todos los turnos que le queden a la sesión. Es la otra cara de por qué Claude Code consume tantos tokens: allí lo que se cuenta es que el historial entero se reenvía en cada turno; aquí, que eres tú quien decide cuánto engorda ese historial, y el @ repetido es la forma más fácil de engordarlo sin enterarte.
Cómo se usa entonces
Escribe el @ en el turno en el que el fichero tiene que entrar. A partir de ahí, nómbralo con palabras:
turno 1 revisa @src/auth/middleware.ts ← aquí entra
turno 2 cambia el refresh del middleware ← ya lo tiene
turno 3 y añade el test ← sigue teniéndolo
Funciona porque el adjunto del turno 1 sigue en la conversación. No hace falta reenviarlo para que lo vea.
Hay dos casos en los que repetir el @ sí vale lo que cuesta:
- El fichero ha cambiado en disco. Comprobado: la segunda mención relee de disco, no repite la copia vieja. Toqué el fichero entre dos turnos y el segundo adjunto traía lo nuevo. Si Claude tiene la foto vieja, vuelve a mencionarlo.
- Después de un
/compacto un/clear. Ahí el adjunto viejo puede haber desaparecido del contexto, así que la copia hace falta otra vez. Qué sobrevive exactamente a un compact está en las instrucciones que desaparecen sin avisar.
Y si lo que necesitas es orientarte y no leer, apunta a un directorio en vez de a un fichero: @src/api/ devuelve el listado de nombres, no el contenido de cada uno. Esa y las otras formas de dar contexto están en cinco formas de darle a Claude Code el contexto correcto.
Requisitos
- Medido en Claude Code v2.1.251, sobre una sesión nueva.
Docs oficiales: Reference files and directories