CommunityImagengithub.com

darkhipo/sharethought

Empaqueta datos en capas dentro de un PNG. La imagen que ve el humano es la superficie SVG renderizada; la fuente SVG y un documento markdown viajan dentro del archivo, en fragmentos iTXt que sobreviven a los procesos de imagen y en un trailer greppable después de IEND. Úsalo para crear, desempaquetar, editar o reempaquetar archivos sharethought: artefactos autocontenidos para el intercambio colaborativo humano-LLM donde el humano lee la imagen y el LLM lee la profundidad.

¿Qué es sharethought?

La habilidad 'sharethought' permite incrustar datos estructurados (como código fuente SVG y documentos Markdown) dentro de un archivo PNG, manteniendo la imagen visual intacta. Está diseñada para el intercambio colaborativo entre humanos y LLMs: el humano ve la imagen, el LLM accede a los datos ocultos. Soporta dos mecanismos de transporte: un trailer después del chunk IEND (simple, solo para transportes byte exactos) y chunks auxiliares iTXt (que sobreviven a pipelines de imagen). El usuario puede empaquetar, desempaquetar y reempaquetar archivos usando la implementación de referencia `sharethought.py`, que asegura consistencia entre ambos carriers y detecta manipulación externa. Es útil para crear artefactos autocontenidos donde la imagen es la interfaz visual y los datos profundos (documentación, código fuente) están disponibles para el LLM sin canales laterales.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/darkhipo/sharethought/tree/HEAD

Installed? Explore more Imagen skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace sharethought?

Un archivo sharethought es un PNG válido que lleva secciones de texto junto con la imagen. Se abre y renderiza como una imagen normal en todas partes. Detrás de la imagen: la fuente SVG a partir de la cual se renderizó y un documento markdown. Un manifiesto al inicio de los datos transportados indexa todo y lleva las instrucciones de desempaquetado/reempaquetado dentro del propio archivo.

Las secciones pueden viajar en uno de dos transportadores: añadidas después del chunk IEND (simple, solo transportes byte exactos), o dentro de chunks auxiliares PNG (sobrevive a pipelines de imagen). Consulta Transportadores.

Usa la implementación de referencia

sharethought.py en este repositorio es la implementación probada, que solo usa la biblioteca estándar. Prefiérela sobre un análisis hecho a mano — cada reimplementación es una nueva superficie de errores.

python3 sharethought.py pack   --png base.png --svg s.svg --doc d.md -o out.png --what "una línea"
python3 sharethought.py unpack out.png [--out-dir DIR]
python3 sharethought.py repack --text out.png --set doc=new.md -o out2.png
python3 sharethought.py verify out.png
python3 -m unittest -q test_sharethought    # el conjunto de pruebas

pack escribe ambos transportadores a partir de una sola construcción (no pueden divergir). unpack prefiere los chunks, recurre al trailer, y da error si ambos transportadores están presentes pero no coinciden — pack no puede producir eso, así que el desacuerdo significa manipulación externa. verify comprueba la firma, IEND, ambos transportadores, la gramática de los nombres de sección y la consistencia manifiesto↔sección.

Formato (exacto)

[ bytes PNG ... chunk IEND ]
SHARETHOUGHT-v1\n
[ manifiesto JSON — un objeto UTF-8, ver esquema ]\n
<<<SECTION:nombre>>>\n
[ contenido de la sección — UTF-8 bruto por defecto, base64 si "enc":"base64" ]\n
<<<END:nombre>>>\n
... (más secciones, mismo marco)

El marcador SHARETHOUGHT-v1 comienza inmediatamente después del chunk IEND del PNG y termina con un salto de línea. (El byte anterior es la cola CRC del chunk IEND, no un salto de línea — búscalo por byte, no por línea.) Todo desde el marcador hasta el final del archivo es el trailer. Secciones estándar: svg (la fuente de la imagen, image/svg+xml) y doc (markdown, text/markdown). Se permiten secciones adicionales arbitrarias — cualquier tipo de texto.

Los nombres de sección deben coincidir con [A-Za-z0-9_]+. Los nombres fuera de esa gramática se rechazan al empaquetar y se marcan con un error con nombre al desempaquetar — nunca desaparecen silenciosamente.

Transportadores

El mismo manifiesto y las mismas secciones se pueden transportar de dos maneras. Elige según el transporte.

A. Trailer — añadido después del chunk IEND (el predeterminado anterior).

  • Trivial de escribir a mano: cat trailer.txt >> file.png. Sin CRC, sin marco de chunk, sin biblioteca.
  • Sobrevive solo a transportes byte exactos: git, zip, curl -O, una copia directa de archivo.
  • No sobrevive a ninguna pipeline que serialice o recodifique la imagen. Una transcodificación PNG→JPEG lo elimina por completo. Una reescritura a nivel de chunk — que muchas pipelines de chat y drive hacen — conserva cada byte IDAT y descarta todo después de IEND.

B. Chunks auxiliares — iTXt dentro del flujo de chunks PNG.

  • Vive dentro de la lista de chunks, por lo que sobrevive a reescrituras a nivel de chunk y pipelines de imagen que conservan PNG.
  • Cuesta un CRC y un marco de chunk, y las secciones grandes quieren compresión.
  • No sobrevive a una transcodificación PNG→JPEG — nada sobrevive excepto los píxeles.

Recomendación: escribe ambos (pack lo hace). Misma carga, una construcción, no es posible la divergencia. Un lector busca primero los chunks y recurre al trailer, por lo que el archivo funciona tanto en pipelines de chat como en transportes byte exactos. El costo es aproximadamente una copia extra de la carga.

Disposición de chunks:

[ IHDR ] [ IDAT ... ] [ iTXt sharethought:manifest ] [ iTXt sharethought:<nombre> ] ... [ IEND ]

Un iTXt por sección: palabra clave sharethought:manifest para el manifiesto, sharethought:<nombre> para cada cuerpo de sección (cuerpo bruto, sin delimitadores). Marco iTXt estándar — palabra clave \0, flag de compresión (1 byte, 1 = zlib), método de compresión (0), etiqueta de idioma \0, palabra clave traducida \0, luego el texto.

Esquema del manifiesto

{
  "v": 1,
  "what": "una línea que describe este artefacto",
  "how": {
    "unpack": "Lee los chunks iTXt llamados sharethought:* si están presentes (el manifiesto es sharethought:manifest); de lo contrario, lee los bytes desde el marcador SHARETHOUGHT-v1 hasta EOF. Analiza el manifiesto JSON, luego cada sección entre sus delimitadores <<<SECTION:nombre>>> y <<<END:nombre>>>. Decodifica las secciones base64.",
    "repack_text_only": "Conserva los bytes PNG originales hasta el marcador, luego reescribe el trailer con el manifiesto y las secciones actualizados. No se necesita rerenderizado. O ejecuta: python3 sharethought.py repack --text FILE --set doc=new.md",
    "repack_visual": "Para cambiar la imagen: edita la sección svg, renderízala a PNG (rsvg-convert -w 1200 in.svg > out.png, o cairosvg, o resvg, o un navegador sin cabeza como Chromium a través de Playwright), luego empaqueta de nuevo."
  },
  "sections": [
    { "name": "svg",  "type": "image/svg+xml", "enc": "utf8",    "summary": "descripción de una línea de la imagen" },
    { "name": "doc",  "type": "text/markdown", "enc": "utf8",    "summary": "descripción de una línea del documento" }
  ]
}

El campo how es obligatorio y debe contener instrucciones funcionales — el archivo enseña a cualquier lector frío cómo abrirlo.

Desempaquetar

Lo mejor: python3 sharethought.py unpack file.png. Manual, transportador trailer:

raw = open("file.png", "rb").read()
# Recorre los chunks PNG hasta el IEND real — nunca uses raw.index() con el marcador,
# los bytes IDAT comprimidos pueden contenerlo por casualidad.
off = 8
while True:
    import struct
    ln, typ = struct.unpack(">I", raw[off:off+4])[0], raw[off+4:off+8]
    if typ == b"IEND": break
    off += 12 + ln
trailer = raw[off + 12:].decode("utf-8")
import json
manifest, _ = json.JSONDecoder().raw_decode(trailer[trailer.index("\n")+1:])
import re
for m in re.finditer(r"<<<SECTION:(\w+)>>>\n(.*?)\n<<<END:\1>>>", trailer, re.DOTALL):
    name, body = m.group(1), m.group(2)
    # si enc es base64: import base64; body = base64.b64decode(body)

Manual, transportador chunk (se comprueba primero):

import struct, zlib, json
raw = open("file.png", "rb").read()
off, chunks = 8, {}
while off + 8 <= len(raw):
    ln = struct.unpack(">I", raw[off:off+4])[0]
    typ = raw[off+4:off+8]
    data = raw[off+8:off+8+ln]
    if typ == b"IEND": break
    if typ == b"iTXt":
        key, _, rest = data.partition(b"\x00")
        flag = rest[0]
        rest = rest[2:]
        _, _, rest = rest.partition(b"\x00")   # etiqueta de idioma
        _, _, rest = rest.partition(b"\x00")   # palabra clave traducida
        if flag == 1:
            rest = zlib.decompress(rest)
        chunks[key.decode()] = rest.decode("utf-8")
    off += 12 + ln
manifest = json.loads(chunks["sharethought:manifest"])
sections = {k[len("sharethought:"):]: v
            for k, v in chunks.items()
            if k.startswith("sharethought:") and k != "sharethought:manifest"}

Solo con shell: tail -c +$(( $(grep -abo 'SHARETHOUGHT-v1' file.png | head -1 | cut -d: -f1) + 1 )) file.png imprime el trailer como texto (funciona solo cuando el trailer ha sobrevivido al transporte).

Crear

Lo mejor: renderiza el SVG, luego python3 sharethought.py pack --png out.png --svg diagram.svg --doc notes.md -o artifact.png. Manual:

  1. Escribe el SVG (la imagen o diagrama orientado al humano).
  2. Renderiza a PNG: rsvg-convert -w 1200 diagram.svg > out.png (o cairosvg diagram.svg -o out.png, o resvg diagram.svg out.png, o un navegador sin cabeza — Chromium a través de Playwright — que suele ser el único renderizador SVG instalado).
  3. Escribe el documento markdown.
  4. Añade el trailer (forma cat trailer.txt >> file.png) y, mejor, también escribe los chunks iTXt — pack hace ambos a partir de una sola construcción.

La salida se renderiza como un PNG normal. Verifica: ábrelo en cualquier visor de imágenes (muestra la imagen), luego strings artifact.png | grep SHARETHOUGHT (muestra que el trailer existe), o python3 sharethought.py verify artifact.png (comprueba todo, incluyendo que ambos transportadores coinciden).

Reempaquetar

Lo mejor: python3 sharethought.py repack --text artifact.png --set doc=new.md -o out.png — reescribe ambos transportadores, no se necesita renderizador.

Edición solo de texto (doc cambiado, imagen sin cambios): reutiliza los bytes PNG originales (todo antes de SHARETHOUGHT-v1), escribe un nuevo trailer con la sección editada y los resúmenes del manifiesto actualizados. Si el archivo también lleva chunks, reescribe también esos — la implementación de referencia reconstruye ambos desde una sola fuente, que es la única forma segura de mantenerlos coincidentes.

Edición visual (svg cambiado): renderiza el SVG modificado a un PNG nuevo, luego empaqueta de nuevo con la nueva sección svg + doc + manifiesto. Cualquier renderizador SVG funciona, incluido un navegador sin cabeza.

Regla de ida y vuelta: un archivo reempaquetado es un archivo sharethought. Mismo formato, mismo marcador, manifiesto actualizado.

Seguridad de los delimitadores

Los delimitadores de sección son <<<SECTION:nombre>>> y <<<END:nombre>>> en sus propias líneas. Si el contenido pudiera contener una línea así, establece "enc":"base64" en el manifiesto para esa sección y codifica en base64 el cuerpo — pack hace esto automáticamente cuando un cuerpo contiene su propio delimitador de cierre o la cadena del marcador.

Qué colisión realmente rompe: un cuerpo que contiene delimitadores de una sección diferente es inofensivo. Un cuerpo que contiene su propia línea <<<END:nombre>>> es truncado por una expresión regular no codiciosa — todo después de esa línea queda huérfano. La implementación de referencia detecta esto (errores de contenido huérfano y delimitador desbalanceado) en lugar de truncar silenciosamente. En caso de duda, usa base64.

Mantener la carga viva

La carga no forma parte de los datos de píxeles del PNG, por lo que cualquier herramienta que recodifique la imagen puede eliminarla silenciosamente y dejar un PNG de aspecto perfectamente normal sin profundidad. Eso incluye Image.open(f).save(g) en Pillow, editores de imágenes y optimizadores de CDN. Reempaqueta a partir de los bytes originales en lugar de volver a guardar el archivo renderizado.

Los trailers después de IEND sobreviven solo a transportes byte exactos. Si el archivo debe viajar a través de una pipeline de imagen — un adjunto de chat, un generador de miniaturas, un transcodificador — usa el transportador de chunks (pack lo hace), y súbelo como archivo en lugar de como imagen en línea para que el PNG no se transcodifique a JPEG.

Trabajar con un humano

El humano ve el PNG — la capa visual simplificada. Toda la profundidad (fuente SVG, doc, manifiesto) es invisible para la visualización casual pero completamente presente para ti. Cuando el humano pregunta sobre la imagen, desempaqueta y lee el documento para contexto. Cuando el humano quiere cambiar la imagen, edita el SVG y reempaqueta visual. Cuando el humano quiere agregar profundidad, edita el documento y reempaqueta solo texto. El archivo es todo el intercambio: un artefacto, ambas modalidades, sin canales laterales.

Skills relacionados