003-blog-sharer/spec.md

SPEC-003 — Blog sharer (amplificador de contenido → X)

SPEC-003 — Blog sharer (amplificador de contenido)

Id: projects/003-blog-sharer/spec
Carpeta de implementación: projects/003-blog-sharer/ — CLI sharer.mjs (npm start), dependencias en package.json.


Objetivo

Entregar una herramienta CLI (Node.js, coherente con 001-progress-tracker) que permita compartir en X (Twitter) un post del blog local (episodios en Markdown) con:

  1. Un texto de tweet atractivo en castellano, listo para revisar en el compositor web.
  2. Una imagen de tarjeta generada localmente (pipeline Satori → rasterizado PNG), alineada a la identidad Synthcode / Neo-Dev y al modo visual “Artículo” descrito abajo.

No objetivo explícito: publicación automática vía API oficial de X; el flujo es asistido (Web Intent + imagen generada por el usuario), en la línea del tracker (episodio 020).


Alcance de datos (fuente de verdad)

  • Entrada: archivos *.md bajo src/content/docs/system/episodes/ (bitácora técnica publicada en /blog/[slug], donde slug = nombre de fichero sin extensión .md).
  • Parsing: leer y parsear el frontmatter YAML de cada fichero para extraer como mínimo:
    • title — título humano del episodio.
    • description — resumen corto (útil como gancho en el tweet).
    • seoTitle — título orientado a descubrimiento; puede usarse como variante de titular en la tarjeta o en la primera línea del tweet si difiere de title.

Si algún campo opcional falta en entradas legacy, la implementación debe documentar fallbacks (p. ej. title solo, truncado con elipsis; nunca fallar en silencio sin mensaje claro).


Flujo de usuario (MVP)

  1. El usuario ejecuta el script desde el repo: npm start en projects/003-blog-sharer/ (o node sharer.mjs en esa carpeta).
  2. El script enumera los episodios disponibles (*.md en system/episodes/, orden natural por nombre).
  3. El script pregunta con inquirer (lista): «¿Qué post del Log de Ingeniería quieres compartir?» — opciones con title y slug visible.
  4. Tras la elección:
    • Genera el PNG en public/assets/images/shares/<post-slug>.png, donde <post-slug> es el nombre del fichero sin .md (p. ej. 018-seo-y-busqueda-profunda.png).
    • Construye el texto del tweet en castellano (tono técnico-accesible, sin clickbait engañoso; puede combinar title, description, seoTitle y URL pública del post).
    • Abre el Twitter/X Web Intent (https://twitter.com/intent/tweet?text=...) con el texto codificado (encodeURIComponent), más enlace a https://caprini.dev/blog/<post-slug> (o base configurable por env, alineado a CAPRINI_SITE_BASE / patrones del tracker).
  5. Opcional (recomendado, misma semántica que SPEC-001): variable TWITTER_INTENT_NO_OPEN=1 para no invocar open en entornos sin GUI.
  6. Opcional: al finalizar, confirmación «¿Quieres reconstruir la web ahora? (y/n)» — si se confirma, ejecutar npm run build desde la raíz del repo.

Nota: Adjuntar la imagen al tweet sigue siendo gesto humano en la UI de X; la spec solo exige que el fichero quede en ruta predecible para arrastrar o subir.


Módulo visual — «Modo Artículo» (Satori)

Formato

  • Dimensiones: 1200×675 px (mismo ratio que la tarjeta del progress tracker; coherencia con previews de enlaces en X).

Estética (normativa de producto)

ElementoEspecificación
FondoImagen public/assets/images/bg-share.png incrustada como data URL en Satori (textura técnica sobre el lienzo 1200×675).
TitularTexto del post en grande; color cian neón + text-shadow cian (glow); fuente JetBrains Mono (SPEC-001 / episodio 012).
ComposiciónBarra de estado superior (STATUS: LOG_DECRYPTED // CAPRINI_OS); titular y slug centrados; logo-writer.png en esquina inferior; marco doble 1px cian / 1px púrpura (sensación de profundidad).
Metadatos opcionalesLínea secundaria discreta (p. ej. description truncada o seoTitle) en color de cuerpo legible sobre oscuro (gris muy claro tipo #e6edf3), sin robar protagonismo al titular.

Pila técnica (decisión heredada)

  • Satori + rasterizado (@resvg/resvg-js u homologación ya usada en el repo), reutilizando el patrón probado en projects/001-progress-tracker/tracker.mjs donde aplique.
  • Fuentes: JetBrains Mono reutilizada desde projects/001-progress-tracker/assets/fonts/JetBrainsMono-Regular.ttf (misma licencia que SPEC-001).

Criterios de aceptación (implementación futura)

  • PNG estable en public/assets/images/shares/<slug>.png tras elegir un episodio.
  • Tarjeta reconocible como “artículo del blog” (titular cian + lectura + Carpincho).
  • Tweet prefill en castellano + URL /blog/<slug>; sin credenciales de X en el script.

Variables de entorno (propuesta)

Definir en .env.example del proyecto o en la raíz del repo al implementar (valores de ejemplo sin secretos):

VariableRol
CAPRINI_SITE_BASEOrigen del sitio (p. ej. https://caprini.dev) para componer la URL del post.
TWITTER_INTENT_NO_OPENSi está definida a 1, no abrir el navegador tras generar la URL.

Referencias cruzadas

  • Branding: src/content/docs/system/BRANDING.md
  • SEO / frontmatter: .cursor/rules/10-seo-standards.mdc, src/content.config.ts
  • Patrón Intent + open: src/content/docs/projects/001-progress-tracker/spec.md, episodio 020
  • Bitácora del hito de especificación: episodio 021src/content/docs/system/episodes/021-amplificando-el-contenido.md

Condición de cierre de esta fase

Implementación verificada en local: CLI, PNG en public/assets/images/shares/<slug>.png, Web Intent + open. Bitácora: episodio 022022-el-script-que-da-voz.md.