Operación del sistema MarkeUP

doc-operacion-MarkeUp

Operación de MarkeUP: comandos de Hermes y troubleshooting diario

Los comandos de gestión del vault con Hermes Agent, los scripts principales del proyecto, el troubleshooting más habitual y el flujo típico de publicación diaria.

Admin15 de junio de 2026, 00:0027 min de lectura

Orden de lectura recomendado: doc-readme → doc-guia → doc-arquitectura → doc-buscador → doc-operacion → doc-publicar → doc-auditoria


Hermes Agent es el asistente IA que gestiona el vault. Corre sobre Ollama (modelo qwen2.5-coder:7b-64k). El sistema de reglas y skills vive en src/content/hermes/.

# Iniciar Hermes (requiere Ollama corriendo)
hermes chat

# Estado del sistema
hermes status

ComandoSkill usadoResultado
Crea un post sobre [tema] en [categoría]crear-post.mdPost nuevo con frontmatter 22 campos + wikilinks
Amplía [slug] con [tema]ampliar-post.mdSección nueva en nota existente
Revisa los wikilinks de [slug]generar-wikilinks.mdEscanea y enriquece enlaces `[[slug
Audita el vaultauditar-vault.mdInforme de salud con huérfanos y recomendaciones
Revisa la documentacióndocumentar.mdVerifica que los 5 docs reflejen la realidad
¿Qué posts debería crear?—Consulta memory/backlog.md (22 ideas priorizadas)

ErrorCausaSolución
Hermes no respondeOllama no está corriendoollama serve o brew services start ollama
model not foundModelo no descargadoollama pull qwen2.5-coder:7b-64k
Error Connection refusedOllama en otro puertoVerificar ~/.hermes/config.yaml: base_url: http://127.0.0.1:11434/v1
Hermes lentoModelo pesado para el hardwareCerrar otras apps. ollama ps para ver uso

npm run dev            # Servidor local en localhost:4321 con hot reload
npm run dev -- --host  # Servidor accesible desde otros dispositivos en la red
npm run dev:tunnel     # Dev server en la LAN + túnel HTTPS: devuelve los dos enlaces
npm run preview        # Previsualiza el build de producción localmente
npm test               # Ejecuta tests unitarios (Vitest, 6 tests)
npm run test:watch     # Tests en modo watch (re-ejecuta al guardar)

npm run dev:tunnel es la vía cómoda para probar en el móvil o enseñar el sitio desde otra red: levanta el dev server en 0.0.0.0 y un túnel HTTPS hacia el mismo puerto, y devuelve dos enlaces (el de la WiFi y el público). Se para con npm run dev:tunnel:stop, se consulta con npm run dev:tunnel:status (exit 0 = sigue sirviendo, y sirve como gate de un /goal) y se diagnostica con npm run dev:tunnel:setup. Guía de uso en llano: Dev tunnel; manual del repo: docs/dev-tunnel.md.

npm run build          # Build completo: images + astro + pagefind
npm run build:astro    # Solo build de Astro (HTML estático)
npm run build:search   # Regenerar el índice Pagefind (solo artículos, --force-language es)

El build completo genera ~380 páginas HTML en dist/ y, de forma declarativa:

  • /llms.txt — lista de páginas y posts para consumidores LLM (se regenera en cada build).
  • /api/posts.json — manifiesto ligero con slug/título/descripción/metadatos/minutos (usado por rich previews y agentes).
  • /faq, /privacy, /terms, preview.js y rich.css se generan/complían automáticamente en su ruta pública.

Los rich tooltips y rich cards leen /api/posts.json en tiempo de ejecución (fetch perezoso en el navegador). No requieren servidor extra.

npm run build:astro
node scripts/mjs/a11y-check.mjs         # auditoría básica de accesibilidad en dist/
npm run validate:share              # OG + Twitter Cards + URLs de compartir en dist/
python3 -m json.tool dist/api/posts.json >/dev/null   # valida el manifiesto JSON

npm run images         # Procesa imágenes: crop 1920×1080 + variantes responsivas + WebP
npm run images:watch   # Modo vigilancia continua
npm run images -- --force  # Reprocesa todas (ignora caché)

npm run compress-audio         # Procesa pendientes/incorrectos
npm run compress-audio:watch   # Modo vigilancia
npm run compress-audio:force   # Recomprime todos
npm run media:fetch            # Descarga los pósteres de los embeds (public/img/media/)
npm run films:fetch            # Fichas de película desde TMDB (necesita TMDB_TOKEN)
npm run validate:media         # Gate de medios embebidos: bloques, archivos y CSP
npm run content:cache          # Invalida la caché de render de Astro (inputs externos)

compress-audio.js es idempotente: solo convierte lo que falta o lo que no cumple los valores objetivo (≤ 24 MB, 24-120 kbps, ≤ 2 canales). Un MP3 que supera el límite o que quedó con bitrate degradado se detecta y se rehace; los que ya están bien se omiten sin reescribirse.

Límite real de Cloudflare Pages: 25 MiB por archivo. El default es 24 MB por margen. Si un audio no cabe ni en el escalón más bajo (24k mono), el script lo avisa con las opciones disponibles (trocear, subir --max-size, o alojar fuera). Cloudflare Pages no soporta range requests, así que el reproductor descarga el archivo completo antes de sonar: conviene mantener los MP3 pequeños.

Seguridad de los originales. El archivo fuente (.m4a, .wav…) se borra solo después de que la salida haya pasado la validación. Si la conversión falla o el resultado no es válido, el original y el MP3 anterior quedan intactos. Usa --keep-originals para conservarlos siempre.

Salida con código 1 si algún audio no cumple (útil en CI).

node scripts/js/compress-audio.js --dry-run          # Informa sin escribir
node scripts/js/compress-audio.js --bitrate 96k      # Bitrate objetivo (ABR)
node scripts/js/compress-audio.js --max-size 24      # Límite en MB
node scripts/js/compress-audio.js --keep-originals   # No borra los originales
node scripts/js/compress-audio.js --help             # Ayuda

Un --max-size o --bitrate inválido aborta con error, no se ignora: un valor no numérico produciría NaN y desactivaría en silencio todas las comprobaciones de tamaño, dando por buenos audios que romperían el despliegue.

Los reproductores ya no se escriben a mano: un audio o un vídeo es un bloque cercado (audio, video, embed, book, film) que el plugin remark-media convierte en HTML, con su ficha de título, pie y descarga. npm run validate:media es el gate: comprueba que el archivo exista, que viva en una carpeta pública permitida, que no pase de 25 MiB, que el frame-src de la CSP cuadre con los proveedores admitidos y que no haya más de cuatro embeds de terceros por nota. La sintaxis de cada bloque, con sus opciones y sus límites, está en la guía de medios embebidos.

validate:media cubre también el HTML escrito a mano: cualquier <audio> o <source> crudo que aparezca en una nota se comprueba igual (existe el archivo, carpeta permitida, límite de 25 MiB). Antes había un validate:audio solo para eso; se retiró el 2026-10-03 porque era la misma comprobación repetida. Sin ella, un reproductor crudo roto no da error visible: el control aparece, se pulsa play y no suena nada, que es justo lo que delata el gate.


Los scripts están organizados en scripts/ por extensión, para localizarlos rápido:

scripts/
├── sh/    ← Bash  (lib/common.sh = utilidades compartidas)
├── py/    ← Python
├── js/    ← JavaScript
└── mjs/   ← ES Modules (.mjs)
ScriptPropósito
js/process-images.jsCrop 1920×1080 + 3 tamaños WebP (1920/1280/640, calidad 82)
js/sync-images.jsLimpiador de imágenes huérfanas (public/img/). Dry-run por defecto; --apply borra; --include-sources incluye fuentes
mjs/og-cards.mjsGenera tarjetas Open Graph 1200×630 (PNG) por post
mjs/validate-share.mjsAudita OG + Twitter Cards + URLs absolutas sobre dist/ (bloqueante CI)
mjs/validate-slugs.mjsDetecta colisiones de slug/alias entre posts (bloqueante CI)
mjs/validate-wikilinks.mjsValida sintaxis y estilo de los wikilinks
mjs/validate-icons.mjsVerifica que el mapa de iconos coincida con los tipos
mjs/a11y-check.mjsAccesibilidad estática sobre dist/ (h1, labels, alt, noopener…)
js/compress-audio.jsCompresión ffmpeg (límite 25MiB Cloudflare)
mjs/validate-media.mjsGate de medios embebidos: bloques embed/audio/video/book/film, archivos locales, CSP ↔ proveedores (bloqueante)
mjs/fetch-media.mjsDescarga los pósteres de los embeds a public/img/media/ (media:fetch)
mjs/fetch-films.mjsPuebla la caché de fichas de película desde TMDB (films:fetch; necesita TMDB_TOKEN)
mjs/content-cache.mjsInvalida la caché de render de Astro cuando cambia un input externo a las notas (content:cache)
js/export-blog.jsExportar posts por categoría
js/dev-search.jsÍndice Pagefind + copia del grafo de búsqueda para desarrollo (npm run dev:search)
sh/hermes-connect.shNuevo — Arranca Hermes + su gateway de Telegram
sh/telegram-connect.shTerminal del Mac por Telegram (npm run term): bot propio, init, check, install, logs, policy
py/telegram-terminal.pyEl bot de la terminal: ejecuta shell en el Mac desde el móvil, con política de seguridad
py/enrich-wikilinks.pyEnriquece wikilinks (npm run wikilinks:enrich)
py/subset-fonts.pySubset de fuentes a WOFF2 (npm run fonts:subset)
sh/publish.shPublica: valida, commit y push
sh/auto-commit.shSincronización automática (imágenes → commit → push)
sh/syncthing-connect.shConecta y gestiona Syncthing

npm run test            # tests unitarios (vitest)
npm run validate:slugs  # slugs/aliases únicos
npm run validate:share  # OG + Twitter Cards + URLs de compartir
npm run test:a11y       # accesibilidad sobre dist/
npm run sync:images     # imágenes huérfanas (dry-run)
npm run prettier        # formatea el código
npm run lint            # eslint

La generación automática de artículos ya no la hace un script suelto: la hace Hermes Agent con el atajo /markeup-post (contexto, frontmatter, cuerpo, enlaces, medios, imagen, SEO y validación). Así el resultado pasa por el mismo pipeline validado que el resto del vault.

npm run hermes                 # abre Hermes (o usa el bot de Telegram)

Y le pides en lenguaje natural:

crea un post sobre <tema> en Máquina
amplía <slug> con <tema>
revisa los wikilinks de <slug>

ParámetroValorNota
modelqwen2.5-coder:7b-64kSegún ~/.hermes/config.yaml
num_ctxconfigurableUn contexto alto consume mucha RAM
temperature0.7—

Hermes también puede escribir en el vault desde Telegram, usando las mismas skills. Ver la sección 6.1.


  1. Escribir/editar en Obsidian o pedírselo a Hermes
  2. Syncthing sincroniza al Mac
  3. Si hay imágenes nuevas: npm run images
  4. Verificar: npm run build (debe dar 0 errores)
  5. Publicar: npm run publish -- -m "tipo(alcance): descripción" — valida (tests + slugs), construye, hace commit y sube a GitHub en un paso. Añade --dry-run para simular sin cambios, --no-verify para saltar validaciones.
  6. GitHub Actions valida → Cloudflare Pages despliega automáticamente

Publicar desde Hermes (CLI o Telegram): existe la skill markeup-deploy (y el atajo /markeup-publish, que además audita y corrige), que encapsula el push a GitHub y el despliegue a Cloudflare con validación previa y verificación posterior. Basta pedirle «haz push y deploy».

El script scripts/sh/publish.sh sustituye a los antiguos deploy.sh y commit-local.sh. No usa wrangler porque Cloudflare Pages ya construye al recibir el push.


Los scripts de integración tienen nombres descriptivos: cada uno gestiona una conexión externa (los dos bots de Telegram y Syncthing) y todos sus comandos tienen un atajo npm equivalente.

Telegram tiene dos consumidores distintos, cada uno con su bot y su token (Telegram admite un solo proceso por token):

BotQuién lo atiendePara qué
El del agenteGateway de Hermes (esta sección)Hablar con el agente: crear posts, auditar el vault, usar sus skills
El de la terminalBot propio (npm run term, § 6.2)Ejecutar comandos de shell en el Mac desde el móvil y leer la salida

Al escribir al bot del agente hablas con Hermes, con acceso a todas sus skills (markeup-content, markeup-audit, markeup-wikilinks…), memoria y herramientas.

npm run hermes                 # arrancar (atajo)
npm run hermes -- check        # diagnóstico
npm run hermes -- stop
npm run hermes -- restart
npm run hermes -- status
npm run hermes -- logs
npm run hermes -- setup        # reconfigurar Telegram (solo si cambias de bot)
ComandoQué hace
(ninguno)Arranca Hermes + Telegram
startArranca el gateway y repara el servicio si está obsoleto
checkDiagnóstico: binario, token, skills, conflictos y vault
stop / restartCiclo de vida
statusGateway, Telegram y arranque automático
logstail -f del log del gateway
setupAsistente de Hermes para reconfigurar plataformas

No pide tokens. La configuración vive una sola vez en ~/.hermes/.env:

VariableContenido
TELEGRAM_BOT_TOKENToken del bot (de @BotFather)
TELEGRAM_ALLOWED_USERSIDs de Telegram autorizados

⚠️ Telegram solo admite un proceso por bot. Si arrancas otro bot con el mismo token, el gateway se cae con Conflict: terminated by other getUpdates. Por eso el token ya no está en el .env del repo: la fuente única es ~/.hermes/.env. El comando check detecta este conflicto.

Reparación automática del servicio. Hermes instala un servicio launchd (ai.hermes.gateway) que guarda la ruta absoluta de su intérprete. Si el proyecto se mueve, esa ruta deja de existir y el servicio falla. start detecta el caso y reinstala el servicio por su cuenta (hermes gateway install --force --start-now --start-on-login).

Contexto del vault. Para que Hermes conozca las reglas del repositorio (categorías válidas, Regla de Oro de wikilinks, comandos de validación), existe AGENTS.md en la raíz. Hermes lo carga automáticamente al trabajar aquí.

El bot de Hermes no ejecuta shell: te conecta con el agente. Para trabajar en el Mac desde el móvil (lanzar comandos, validar, mirar el log, comprobar el disco) hay un bot propio, con su token, que hace long-polling contra la API de Telegram: sin webhooks, sin puertos abiertos y sin depender de la nube.

npm run term -- init                    # crea el archivo de claves (chmod 600)
npm run term -- check --deep            # token, API, política y selftest
npm run term -- install --keep-awake    # servicio launchd (arranca al iniciar sesión)
npm run term -- status                  # ¿corriendo? ¿modo? ¿estado?
npm run term -- logs 200                # log del bot
DóndeQué
Claves (fuera del repo, permisos 600)~/.config/markeup/telegram-terminal.env
Bot (solo librería estándar de Python)scripts/py/telegram-terminal.py
Gestor (init, check, install, logs, policy)scripts/sh/telegram-connect.sh
Estado y log~/.local/state/markeup/telegram-term/

En el chat, el texto libre se ejecuta en la shell y los atajos atienden el resto: /status, /id, /cd, /pwd, /mode, /policy, /cancel y /acciones (botones rápidos). Lo peligroso pide botón ✅/🚫; además hay denylist dura, autorización por from.id (solo chats privados), timeout con grupo de procesos propio, redacción de secretos en la salida y un solo comando en vuelo. sudo no funciona (no hay TTY) y los comandos interactivos se cierran.

⚠️ El token de la terminal no puede ser el de Hermes: Telegram admite un solo consumidor por token y el otro proceso recibe 409 Conflict. npm run term -- check compara los dos y avisa. El token se valida al pegarlo (init): el fallo típico es pegarlo dos veces en el prompt silencioso, que Telegram contesta con un 404 Not Found que parece un problema de red.

El manual completo (uso diario, referencia de comandos y variables, seguridad, mantenimiento y problemas típicos) vive en docs/telegram-terminal.md, y la ayuda del día a día está en el propio chat (/help) y en npm run term -- --help.

Syncthing sincroniza src/content/posts/ entre el Mac y Android (Obsidian) de forma peer-to-peer.

npm run syncthing                # detecta y arranca
npm run syncthing -- check       # diagnóstico
npm run syncthing -- id          # Device ID de este Mac
npm run syncthing -- folder      # carpeta a compartir + pasos
npm run syncthing -- open        # interfaz web (localhost:8384)
npm run syncthing -- install     # instala con Homebrew (pregunta antes)
npm run syncthing -- install-service
ComandoQué hace
(ninguno)Detecta el binario y arranca
checkDiagnóstico (binario, config, proceso, servicio, interfaz, carpeta)
idDevice ID del Mac — necesario para emparejar con Android
folderRuta exacta a compartir + pasos guiados en Mac y Android
openAbre la interfaz web
installbrew install syncthing (siempre pregunta antes)
install-service / uninstall-serviceArranque automático

El script detecta la instalación automáticamente, en este orden: PATH → /Applications/[[syncthing-solucion-segura-abierta|Syncthing]].app → /opt/homebrew/bin → /usr/local/bin. Así funciona tanto si Syncthing viene de Homebrew como si es la app de macOS.

Para saber si Syncthing está realmente operativo, el script no se fía de que el proceso exista: consulta la API REST (127.0.0.1:8384). Es la única señal de que la interfaz responde de verdad.

EstadoSignificado
OPERATIVOEl proceso vive y la interfaz responde
ARRANCANDOHay proceso pero la API aún no contesta
DETENIDOSin proceso

Esto evita el falso positivo de comprobar solo el proceso, donde el script podía decir “ya está corriendo” mientras la interfaz no respondía.

Al arrancar, prueba las vías en orden hasta que una funcione:

  1. brew services — si es una instalación de Homebrew
  2. open -a [[syncthing-solucion-segura-abierta|Syncthing]] — si es la app de macOS (la GUI)
  3. Daemon en segundo plano — binario suelto, con PID y log propios

La app de macOS puede tardar 1-2 minutos en levantar la interfaz (arranque de la GUI + generación de claves + indexado inicial). El script espera hasta 120 s y te avisa de que es normal.

Nunca instala nada sin permiso: install pide confirmación explícita y el resto de comandos solo leen/arrancan/paran procesos existentes.

Los scripts devuelven código ≠ 0 cuando algo falla, para que el error sea detectable desde otros scripts o desde CI. Antes un fallo terminaba en 0 y pasaba desapercibido.

Las skills son flujos que Hermes carga automáticamente según lo que le pidas. El canon vive en ~/.hermes/skills/markeup/<nombre>/SKILL.md —una carpeta por skill, enlazada desde src/content/hermes/skills— y son 14 skills atómicas más 3 atajos, con su índice navegable en ~/.hermes/skills/INDEX.md.

Los atajos (bundles, en ~/.hermes/skill-bundles/) son la forma corta de pedir un flujo entero: cargan varias skills de golpe.

AtajoPara quéEjemplo de petición
/markeup-postCrear una nota completa«crea un post sobre X en Máquina»
/markeup-publishAuditar, corregir y publicar«audita el vault y publica»
/markeup-verifyPasada de gates, en solo lectura«¿cómo está el vault?»

Qué sabe hacer cada una de las catorce, con qué frase se pide y su ficha pública: la guía Hablar con Hermes: qué pedir y cómo. El manual técnico del sistema —canon, gate y las dos proyecciones— es docs/skills-system.md del repo.

Añadir o editar una skill: crea o edita ~/.hermes/skills/markeup/markeup-<x>/SKILL.md con el frontmatter del canon (name igual que la carpeta, description de 60 caracteres y con punto, tags y related_skills que resuelvan) y documéntala en ~/.hermes/skills/INDEX.md, en markeup/DESCRIPTION.md y en docs/skills-system.md. Ciérralo con npm run skills:check (canon, comandos reales, referencias y copia de Cline) y regenera las copias con npm run skills:cline y npm run skills:publish.

Encapsula la publicación (es el último paso del atajo /markeup-publish). Hace el push a main y verifica que Cloudflare haya desplegado, comparando el hash del CSS servido en producción.

npm run publish -- -m "feat(posts): añade artículo sobre X"
npm run publish -- --dry-run -m "prueba"        # simulación
npm run publish -- --check-deploy               # solo diagnosticar

⚠️ Un push a main despliega a producción. La skill exige confirmación explícita del usuario y prohíbe --no-verify o push --force por iniciativa propia.

ErrorCausaSolución
ResourceExhausted: Worker local total request limit reachedEl modelo del proveedor (NVIDIA NIM) está saturadoCambiar a modelo local: hermes config set model.default qwen2.5-coder:7b-64k
El agente responde cosas genéricas tipo «no tengo herramientas para eso»Fallback tras agotar reintentos: el modelo no respondió y emite un texto por defectoRevisar ~/.hermes/logs/errors.log; comprobar el modelo activo
El agente ignora el modelo de config.yamlLa sesión quedó anclada a otro modelo al crearsehermes sessions delete <id> --yes y reiniciar el gateway
Respuestas de 200-300 sModelo remoto saturado o demasiado grandeUsar un modelo local; verificar con curl la latencia real
The token ... was rejected by the serverToken revocado o incorrecto en ~/.hermes/.envGenera uno nuevo con @BotFather y ejecuta npm run hermes -- setup
Conflict: terminated by other getUpdatesDos procesos usando el mismo botnpm run hermes -- check (detecta el conflicto) y para el otro bot
El gateway no arranca y el servicio fallaRuta obsoleta en el plist de launchdnpm run hermes (lo repara automáticamente)
stop parece fallar pero el gateway sí paraKeepAlive de launchd tarda en soltar el procesoCorregido: el script ahora espera y distingue ambos casos
Syncthing no encontradoNo está instaladonpm run syncthing -- install
Dice DETENIDO y no arrancaApp de macOS sin daemon activonpm run syncthing — reinicia la app automáticamente
Estado ARRANCANDO mucho tiempoLa app está indexando o generando clavesEspera 1-2 min; comprueba con npm run syncthing -- status
La interfaz web no respondeProceso vivo pero API caídanpm run syncthing -- stop y luego npm run syncthing
No sincroniza con AndroidDevice ID o Folder ID distintosnpm run syncthing -- folder (revisa el Folder ID)

Cuando el agente responde algo raro, lo primero es saber qué modelo está usando de verdad:

hermes config get model.default      # el configurado
hermes config get model.provider

# ¿Qué modelo usó cada sesión? (la sesión puede tener otro anclado)
python3 -c "
import sqlite3
c = sqlite3.connect('$HOME/.hermes/state.db')
for r in c.execute('SELECT session_id, model, billing_provider FROM session_model_usage ORDER BY last_seen DESC LIMIT 5'):
    print(r)
"

# Errores del proveedor
tail -20 ~/.hermes/logs/errors.log

⚠️ Lección aprendida: una respuesta genérica del tipo «no tengo herramientas para eso» no significa que falte la skill. Suele ser el texto de fallback que Hermes emite cuando el proveedor del modelo falla tras agotar los reintentos. Se reconoce porque menciona herramientas que no existen en la instalación real.


ErrorCausa probableSolución
ZodError en frontmatterCampo inválido o faltanteRevisar schema en content.config.ts
Cannot find moduleDependencia no instaladanpm ci
ENOENT en imagenImagen referenciada no existeVerificar public/img/
Error en wikilinkSlug inexistenteCorregir enlace o crear el post destino
Un [[slug]] sale como huérfano pese a existirEl slug difiere (mayúsculas/caracteres)Normalizar slug en frontmatter del destino y npm run build
Los tooltips/rich cards no aparecenpreview.js no cargado o manifiesto antiguoSubir public/preview.js, limpiar caché, revisar Network («manifest 200 JSON»)
El tooltip se crea en el DOM pero no se ve (queda arriba/fuera).lp-pop posicionado fuera del viewportDesde el fix, .lp-pop usa position: fixed y place() se ancla a coordenadas de viewport (compatible con Lenis) con recorte final: la tarjeta nunca se sale del viewport. Verificar que el CSS compilado tenga .lp-pop{position:fixed}
El tooltip se cierra al ir del enlace al popCierre por pointerout al cruzar el huecoCubierto por removePopSoon() + elementFromPoint en preview.js. No hay que tocar nada

SíntomaCausa probableSolución
WhatsApp/LinkedIn comparten /posts/slug (sin dominio)El componente recibió una URL relativaUsar URL absoluta; buildShareLinks() lanza si no lo es. Ejecutar npm run validate:share
El preview en X/Facebook no muestra imagen grandeog:image no absoluta, o ratio ≠ 1.91:1npm run validate:share; regenerar imagen con npm run og (1200×630)
El autor no aparece como atribución en XFalta twitter:site/twitter:creator o no es @usuarioRevisar TWITTER_HANDLE en BaseLayout.astro; el validador exige formato @usuario
Preview antiguo tras publicarCaché del crawlerRe-scrape manual (Facebook Debugger / X Card Validator)

Diagnóstico de compartir (2026-11-09): los botones propagaban una ruta relativa (/posts/<slug>) a los endpoints de cada red, por lo que WhatsApp compartía literalmente /posts/caretas-mascaras sin dominio (enlace roto) y LinkedIn/Facebook ignoraban la URL. Fix: la URL se construye absoluta en [...slug].astro (${siteOrigin}/posts/${slug}), se normaliza en SocialShare.astro contra Astro.site y se valida en buildShareLinks() (lanza si no es absoluta). Verificado en dist/: los 5 endpoints llevan https%3A%2F%2Fmarkeup.pages.dev%2Fposts%2F.... Añadido npm run validate:share para detectarlo en CI.

Rich previews: los tooltips y las rich cards (![[post-slug]]) necesitan que dist/api/posts.json exista (lo genera build:astro). Si prueba en npm run dev y ves .rich-card-wait sin hidratar, asegúrate de que /api/posts.json responde 200 en el navegador (pestaña Network). El script hace fetch perezoso, así que el primer tooltip puede tardar ~150 ms en mostrarse.

Diagnóstico de tooltips (2026-09-10): el fallo «no van los rich tooltips» era de posicionamiento: .lp-pop era position: absolute añadido a <body> pero colocado con coordenadas de viewport (getBoundingClientRect). Con Lenis (smooth scroll) activo, el pop se dibujaba en top negativo y quedaba invisible aunque existiese en el DOM. Fix: .lp-pop → position: fixed, place() con visualViewport, findHit() sin abortar el ascenso al topar con un <a> no-preview, y cierre diferido con comprobación de elementFromPoint para permitir el «puente» enlace→tarjeta.

Reescritura de la animación (2026-09-24 · verify:preview): la coreografía anterior animaba top y height de la caja con backdrop-filter encima, así que ir fino era imposible. La nueva («la tarjeta se materializa y su foto se abre») anima solo transform, opacity y clip-path, mide el tamaño una sola vez, renuncia al backdrop-filter, reutiliza la misma tarjeta al cambiar de enlace (vuela al enlace nuevo y re-revela su contenido) y suelta will-change al terminar. Medido en Chrome real: p50 17 ms / p95 18 ms, sin picos.

curl http://localhost:11434/api/tags   # ¿Responde?
ollama list                              # Modelos instalados
ollama ps                                # Modelos en ejecución
sysctl vm.swapusage                       # Swap en uso (si es alto, falta RAM)

La redacción con IA tarda 1-3 minutos: es normal. Deberías recibir el progreso paso a paso en el chat.

Si no llega ningún mensaje intermedio y todo se queda en «Procesando…», era un bug ya corregido: el callback de progreso se invocaba desde un hilo secundario con asyncio.create_task, que no lo adjuntaba al bucle de eventos y nunca se ejecutaba (avisaba con “coroutine was never awaited”). La forma correcta es asyncio.run_coroutine_threadsafe.

VariableValor por defectoPor qué
OLLAMA_NUM_CTX8192Contexto. Un valor alto multiplica la RAM de la KV cache
OLLAMA_NUM_PREDICT3000Tope de tokens a generar; evita esperas eternas
OLLAMA_TIMEOUT4207 min de margen antes de abortar
OLLAMA_MODELqwen2.5-coder:7b-64kModelo usado

En un equipo con 16 GB de RAM, un contexto de 65536 tokens empuja al sistema a swap (se midieron ~2,5 GB en uso) y la generación se vuelve lentísima. Si tienes margen de memoria, puedes subirlo:

OLLAMA_NUM_CTX=32768 npm run hermes

Medición de referencia: con num_ctx=8192 el modelo cargado ocupa 5,0 GB de VRAM y la generación completa tarda ~2 minutos. Con 65536 subía a 6,4 GB y se entraba en swap.


ServicioFunciónEstado
com.markeup.process-imagesProcesa imágenes al detectar cambios✅ Activo
ai.markeup.autopublishAutopublica los cambios de src/content/posts/ (debounce 30 s · rate limit 120 s)Apagado desde el 2026-09-24 · reactivar con npm run autopublish -- enable
ai.hermes.gatewayHermes + Telegram al iniciar sesiónOpcional (npm run hermes)
com.markeup.syncthingSyncthing al iniciar sesiónOpcional (npm run syncthing -- install-service)
com.user.watchexecAuto-commit al detectar cambios❌ Descargado
launchctl load ~/Library/LaunchAgents/com.markeup.process-images.plist
launchctl unload ~/Library/LaunchAgents/com.markeup.process-images.plist
launchctl list | grep markeup

VariableUsoDónde se configura
ADMIN_PASSWORDHash de contraseña del panel adminGitHub Secrets
JWT_SECRETClave para firmar tokens JWTGitHub Secrets
BOT_TOKENToken del bot de Telegram.env local (nunca en git)
PUBLIC_CF_ANALYTICS_TOKENBeacon de analítica Cloudflare.env / Cloudflare Pages
MARKEUP_VAULT_ROOTRuta alternativa del vault (opcional)Entorno

DocumentoContenido
doc-readme-MarkeUpVisión general y stack
doc-guia-MarkeUpInstalación y configuración
doc-arquitectura-MarkeUpArquitectura y componentes
Medios embebidosMedios embebidos: bloques, azúcar de Obsidian y gate
doc-auditoria-MarkeUpAuditoría y deuda técnica

1  PROCESOS
├── 1.1 PROCESOS OLLAMA         (iniciar, cambiar modelo, estado, detener)
├── 1.2 PROCESOS TELEGRAM       (iniciar/detener bot)
├── 1.3 PROCESOS HERMES         (lanzar, reanudar, cambiar modelo)
└── 1.4 ARRANCAR Ollama + Telegram

2  VAULT → 2.1 ANÁLISIS DEL VAULT (auditar, huérfanos, stats, docs...)

3  CONTENIDO DEL VAULT          (crear, generar artículo, ampliar, wikilinks...)

4  BUILD & ASSETS
├── 4.1 PROCESAR IMÁGENES Y AUDIOS
├── 4.2 BUILD (completo, preview, pagefind)
├── 4.3 DEV SERVER (local, network)
└── 4.4 NPM (8 comandos útiles)

5  GIT
├── 5.1 Ramas (creación y cambio)
├── 5.2 Ramas (limpieza y fusión)
├── 5.3 Estado y preparación
├── 5.4 Commits y deshacer
├── 5.5 Stash
└── 5.6 Remoto

6  CLOUDFLARE
├── 6.1 Autenticación y proyectos
└── 6.2 Despliegues (preview, producción, rama)

7  MANTENIMIENTO                (renombrar, mover, limpiar, exportar, docs)

8  STATUS                       (posts, git, build, Ollama, assets)

ComandoAcciónEquivalente en menú
npm run toolsMenú interactivo completo—
npm run tools:crearCrear post directamente (también --borrador)3.1
npm run tools:crear:borradorCrear borrador/esqueleto sin IA3.1
npm run tools:auditarAuditar vault directamente2.1.1
npm run tools:statsDashboard stats2.1.3
npm run tools:indexRegenerar vault-index.md7
npm run tools:tagfixNormalizar tags automáticamente7
npm run tools:buscarBuscar texto en el vault6
npm run tools:backlinksVer backlinks de una nota3.4
npm run tools:relacionadasNotas relacionadas3.5
npm run tools:huerfanosListar huérfanos prioritarios2
npm run tools:hermesIniciar Hermes Agent1.3.1
npm run tools:ollamaEstado Ollama (JSON)1.1.3
npm run hermesArrancar Hermes + Telegram1.2.1
npm run syncthingConectar/gestionar Syncthing—
npm run syncthingConectar/gestionar Syncthing—

Para el diseño del sistema, consulta la arquitectura; para el contexto general, el README del proyecto.

Véase también: la arquitectura y la visión general para entender el sistema completo.

Enlaces: README y instalación.

Para profundizar en este ámbito, Dýnamis Kínēsis y Enérgeia desarrolla la tríada aristotélica de potencia, movimiento y acto: la arquitectura metafísica del devenir y su lectura en el cuerpo, la.

doc-operacion-MarkeUp

Explora las ramas del artículo

Compartir este artículo