cvool es open source (MIT). Aquí puedes ver exactamente qué pasa cuando subes tu CV.
Pegas texto o adjuntas un PDF en tu navegador. Si es PDF, se envía al backend donde Claude lo lee nativamente. Tu archivo nunca se guarda en disco.
El texto pasa por sanitizeInput() que elimina bytes nulos y caracteres de control. Se trunca a 35,000 caracteres máximo. Rate limiting: 7 peticiones/hora por IP.
Tu CV se envía a Claude con un system prompt que incluye principios éticos explícitos: no discriminar, no inventar, no inflar scores. El prompt completo está en src/lib/prompts/analyze.txt — es auditable.
Un solo API call con temperature: 0 (determinístico). Claude devuelve JSON streamed via SSE con: score (0-100), análisis en 6 dimensiones, fortalezas, mejoras, y un CV completamente reescrito.
El backend parsea el JSON, valida que tenga score + analysis + improved_cv, y lo devuelve al frontend. Si Claude no responde JSON válido, se muestra un error honesto.
Ves tu score, las 6 dimensiones, sugerencias con before/after, y tu CV mejorado listo para copiar. Todo en tu navegador. Nada se almacena.
El código fuente está público. El prompt de IA está en el repo. Los pesos del scoring están documentados.
Cada sugerencia debe referenciar contenido real del CV. Si algo no está en tu CV, no se menciona.
No penalizamos career gaps, caminos no lineales ni educación no tradicional.
Sin base de datos, sin cuentas, sin cookies de tracking. Tu CV se descarta inmediatamente.
cvool es MIT. Puedes clonarlo, modificarlo, y lanzar tu propia versión. Solo necesitas una API key de Anthropic.
git clone https://github.com/pixan-ai/cvool.git cd cvool npm install echo "ANTHROPIC_API_KEY=tu-key" > .env.local npm run dev
Cómo viaja tu CV por cvool, capa por capa — desde tu navegador hasta Claude y de vuelta.
Sin base de datos y sin cuentas. Tu CV entra, se analiza en memoria y se descarta. Todo ocurre en una sola petición que transmite la respuesta mientras se genera.
Pocas piezas, cada una con un trabajo claro. Cero librerías de UI: todo es HTML, CSS y un puñado de dependencias.
Del stack de arriba, esta es la realidad en paquetes de npm — y tenerlos contados es una ventaja, no una limitación.
next · react · react-dom · @anthropic-ai/sdk · partial-json · @vercel/analytics
typescript · eslint · eslint-config-next · tailwindcss · @tailwindcss/postcss · @types/node · @types/react · @eslint/js · @eslint/eslintrc
Ni shadcn, ni Radix, ni MUI. Cada pixel es HTML y CSS escritos a mano.
Qué hace cada bloque. Abre cada carpeta para ver los archivos y su función.
Diez pasos, desde tu clic hasta el resultado. Cada uno trae su detalle técnico si quieres bajar una capa.
Escribes el texto o adjuntas un PDF. Si es PDF, primero pasa por /api/parse, donde Claude extrae el texto.
Hasta que no hay al menos 50 caracteres, el botón sigue desactivado. Todavía no viaja nada.
Una sola petición POST con tu CV (y el puesto objetivo, si lo diste). La respuesta no es un JSON normal: es un stream.
Antes de gastar un solo token: rechaza peticiones enormes, comprueba el origen, limita la frecuencia y limpia el texto.
Tu CV se envuelve en etiquetas para que el modelo lo distinga de las instrucciones, y se le antepone el prompt constitucional.
El modelo no contesta de golpe: va escribiendo el JSON del análisis token a token, y cada fragmento sale de inmediato.
Cada fragmento se reenvía al navegador como un evento Server-Sent Event, etiquetado por tipo: fragmento, progreso, resultado o error.
En vez de esperar al final, el cliente lee el JSON a medio escribir y va mostrando cada sección en cuanto está lista.
Cuando el JSON está completo, el servidor comprueba que tenga la estructura correcta antes de marcarlo como resultado.
El CV mejorado y el diagnóstico aparecen. Tu CV vivió solo en memoria durante la petición; al terminar, se descarta.
Una sola llamada a la API, en streaming. Esto es, en esencia, lo que recibe el modelo:
anthropic.messages.stream({
model: "claude-sonnet-4-6",
max_tokens: 8_000,
temperature: 0,
system: [{ text: analyze.txt, cache_control: "ephemeral" }],
messages: [{ role: "user", content: "<cv_text>…</cv_text>" }],
})Cuatro tipos de evento viajan por el mismo canal abierto:
El JSON está diseñado para que los campos lleguen justo en el orden en que la interfaz los necesita:
Por eso ves primero el idioma y el puntaje, luego las fortalezas y las mejoras, y al final el CV reescrito creciendo letra a letra.
Cuatro elecciones que parecen pequeñas y sostienen todo lo demás.
La espera es output-bound: casi todo el tiempo es el modelo escribiendo ~5.600 tokens. Un modelo más pequeño no lo arregla, así que cambiar de modelo no es una mejora de velocidad.
Un análisis real tarda 60–120s. Si se bajara ese límite, el stream se cortaría a media generación y el resultado nunca llegaría — sin ningún error visible.
Cien segundos mirando un spinner se sienten rotos. Con SSE ves progreso real y secciones apareciendo; además evita que la petición caduque por timeout.
Si la respuesta no es OK, el cliente muestra un mensaje en lugar de volver al formulario en silencio. Un fallo mudo es peor que un error claro.
No es una política, es la arquitectura:
Eso es todo: una app pequeña, defensas claras, un stream y un prompt cuidado. La elegancia está en lo que no tiene.