CommunityImagegithub.com

darkhipo/sharethought

Compressez des données en couches dans un PNG. L'image visible est le rendu SVG, tandis que le source SVG et un document Markdown sont intégrés sous forme de chunks iTXt et d'une queue après IEND. Idéal pour créer, extraire, éditer ou reconditionner des fichiers sharethought — artefacts autonomes pour l'échange collaboratif humain-LLM où l'humain voit l'image et le LLM lit la profondeur.

Qu'est-ce que sharethought ?

Sharethought est un format de fichier qui combine une image PNG avec des données textuelles en couches (source SVG, document Markdown et un manifeste) dans un seul artefact. Le PNG reste visuellement normal pour un humain, mais le LLM peut extraire et manipuler les métadonnées via des chunks PNG iTXt (qui survivent aux pipelines d'images) ou via une queue après le chunk IEND (pour les transferts exacts octet par octet). Ce skill est utile pour les échanges collaboratifs humain-LLM : l'humain voit l'image, le LLM lit le contexte, modifie le SVG ou le document, et repacke le tout sans perdre les données. Disponible pour Claude Code, Cursor, Codex et autres plateformes supportant l'exécution de scripts Python, la mise en œuvre de référence en Python standard (`sharethought.py`) gère le pack, l'unpack et le repack avec validation et détection de falsification, offrant ainsi une solution robuste pour les artefacts auto-suffisants.

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

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait sharethought ?

Un fichier sharethought est un PNG valide contenant des sections textuelles intégrées, en plus de l'image. Il s'ouvre et s'affiche comme une image normale partout. Derrière l'image : le source SVG dont elle est issue, et un document Markdown. Un manifeste au début des données intégrées indexe tout et contient les instructions de dépaquetage/repack dans le fichier lui-même.

Les sections peuvent voyager via deux mécanismes : ajoutées après le chunk IEND (transport simple, exact octet par octet uniquement) ou à l'intérieur de chunks PNG ancillaires (iTXt) qui survivent aux pipelines d'images (recommandé). Voir Mécanismes de transport.

Utiliser l'implémentation de référence

sharethought.py dans ce dépôt est l'implémentation testée et utilisant uniquement la bibliothèque standard. Préférez-la à un parsing artisanal — chaque réimplémentation est une nouvelle surface de bugs.

python3 sharethought.py pack   --png base.png --svg s.svg --doc d.md -o out.png --what "une ligne"
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    # la suite de tests

pack écrit les deux mécanismes en une seule construction (ils ne peuvent pas diverger). unpack préfère les chunks, se rabat sur la queue, et signale une erreur si les deux mécanismes sont présents mais en désaccord — pack ne peut pas produire cela, donc un désaccord signifie une falsification externe. verify vérifie la signature, IEND, les deux mécanismes, la grammaire des noms de section, et la cohérence manifeste↔section.

Format (exact)

[ Octets PNG ... chunk IEND ]
SHARETHOUGHT-v1\n
[ Manifeste JSON — un objet UTF-8, voir schéma ] \n
<<<SECTION:nom>>>\n
[ contenu de la section — UTF-8 brut par défaut, base64 si "enc":"base64" ] \n
<<<END:nom>>>\n
... (autres sections, même encadrement)

Le marqueur SHARETHOUGHT-v1 commence immédiatement après le chunk IEND du PNG et se termine par un saut de ligne. (L'octet avant est la queue CRC du chunk IEND, pas un saut de ligne — donc trouvez-le par octet, pas par ligne.) Tout du marqueur à la fin du fichier constitue la queue. Sections standard : svg (le source de l'image, image/svg+xml) et doc (Markdown, text/markdown). Des sections supplémentaires arbitraires sont autorisées — tout type de texte.

Les noms de sections doivent correspondre à [A-Za-z0-9_]+. Les noms hors de cette grammaire sont rejetés lors du pack et signalés avec une erreur nommée lors de l'unpack — ils ne disparaissent jamais silencieusement.

Mécanismes de transport

Les mêmes manifeste et sections peuvent être transportés de deux manières. Choisissez selon le transport.

A. Queue — ajoutée après le chunk IEND (le défaut ci-dessus).

  • Triviale à écrire à la main : cat trailer.txt >> file.png. Pas de CRC, pas d'encadrement de chunk, pas de bibliothèque.
  • Survit aux transports exacts octet par octet seulement : git, zip, curl -O, copie directe de fichier.
  • Ne survit pas à un pipeline qui ré-sérialise ou ré-encode l'image. Une conversion PNG→JPEG la supprime complètement. Une réécriture au niveau des chunks — que de nombreux pipelines de chat et de stockage font — conserve chaque octet IDAT et jette tout ce qui suit IEND.

B. Chunks ancillaires — iTXt dans le flux de chunks PNG.

  • Vit à l'intérieur de la liste des chunks, donc survit aux réécritures de chunks et aux pipelines d'images qui conservent le PNG.
  • Coûte un CRC et un encadrement de chunk, et les grandes sections nécessitent une compression.
  • Ne survit pas à une conversion PNG→JPEG — rien ne survit à cela sauf les pixels.

Recommandation : écrivez les deux (pack le fait). Même charge utile, une seule construction, aucune divergence possible. Un lecteur cherche d'abord les chunks et se rabat sur la queue, donc le fichier fonctionne à la fois sur les pipelines de chat et les transports exacts octet par octet. Le coût est environ une copie supplémentaire de la charge utile.

Disposition des chunks :

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

Un iTXt par section : mot-clé sharethought:manifest pour le manifeste, sharethought:<nom> pour chaque corps de section (corps brut, sans délimiteurs). Encadrement iTXt standard — mot-clé \0, drapeau de compression (1 octet, 1 = zlib), méthode de compression (0), balise de langue \0, mot-clé traduit \0, puis le texte.

Schéma du manifeste

{
  "v": 1,
  "what": "une ligne décrivant cet artefact",
  "how": {
    "unpack": "Lire les chunks iTXt nommés sharethought:* s'ils sont présents (le manifeste est sharethought:manifest) ; sinon lire les octets du marqueur SHARETHOUGHT-v1 jusqu'à la fin du fichier. Analyser le manifeste JSON, puis chaque section entre ses délimiteurs <<<SECTION:nom>>> et <<<END:nom>>>. Décoder les sections base64.",
    "repack_text_only": "Conserver les octets PNG originaux jusqu'au marqueur, puis réécrire la queue avec le manifeste et les sections mis à jour. Aucun nouveau rendu nécessaire. Ou exécuter : python3 sharethought.py repack --text FILE --set doc=new.md",
    "repack_visual": "Pour modifier l'image : éditer la section svg, la rendre en PNG (rsvg-convert -w 1200 in.svg > out.png, ou cairosvg, ou resvg, ou un navigateur sans tête comme Chromium via Playwright), puis repacker à nouveau."
  },
  "sections": [
    { "name": "svg",  "type": "image/svg+xml", "enc": "utf8",    "summary": "description en une ligne de l'image" },
    { "name": "doc",  "type": "text/markdown", "enc": "utf8",    "summary": "description en une ligne du document" }
  ]
}

Le champ how est obligatoire et doit contenir des instructions fonctionnelles — le fichier enseigne à tout lecteur novice comment l'ouvrir.

Dépaquetage (Unpack)

Meilleure méthode : python3 sharethought.py unpack file.png. Manuel, mécanisme queue :

raw = open("file.png", "rb").read()
# Parcourir les chunks PNG jusqu'au vrai IEND — ne jamais utiliser raw.index() sur le marqueur,
# les octets IDAT compressés peuvent le contenir par coïncidence.
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 est base64 : import base64; body = base64.b64decode(body)

Manuel, mécanisme chunk (vérifié en premier) :

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")   # balise de langue
        _, _, rest = rest.partition(b"\x00")   # mot-clé traduit
        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"}

Avec uniquement le shell : tail -c +$(( $(grep -abo 'SHARETHOUGHT-v1' file.png | head -1 | cut -d: -f1) + 1 )) file.png affiche la queue sous forme de texte (fonctionne uniquement si la queue a survécu au transport).

Création (Create)

Meilleure méthode : rendre le SVG, puis python3 sharethought.py pack --png out.png --svg diagram.svg --doc notes.md -o artifact.png. Manuellement :

  1. Écrire le SVG (l'image ou diagramme destiné à l'humain).
  2. Rendre en PNG : rsvg-convert -w 1200 diagram.svg > out.png (ou cairosvg diagram.svg -o out.png, ou resvg diagram.svg out.png, ou un navigateur sans tête — Chromium via Playwright — qui est souvent le seul renderer SVG installé).
  3. Écrire le document Markdown.
  4. Ajouter la queue (forme cat trailer.txt >> file.png) et, mieux, écrire aussi les chunks iTXt — pack fait les deux en une seule construction.

Le résultat s'affiche comme un PNG normal. Vérification : ouvrez-le dans n'importe quel visualiseur d'images (affiche l'image), puis strings artifact.png | grep SHARETHOUGHT (montre que la queue existe), ou python3 sharethought.py verify artifact.png (vérifie tout, y compris la cohérence des deux mécanismes).

Repack

Meilleure méthode : python3 sharethought.py repack --text artifact.png --set doc=new.md -o out.png — réécrit les deux mécanismes, aucun renderer nécessaire.

Édition textuelle uniquement (doc modifié, image inchangée) : réutiliser les octets PNG originaux (tout avant SHARETHOUGHT-v1), écrire une nouvelle queue avec la section modifiée et les résumés du manifeste mis à jour. Si le fichier contient aussi des chunks, les réécrire également — l'implémentation de référence reconstruit les deux à partir d'une seule source, ce qui est la seule façon sûre de les garder en accord.

Édition visuelle (svg modifié) : rendre le SVG modifié en un nouveau PNG, puis repacker avec la nouvelle section svg + doc + manifeste. N'importe quel renderer SVG fonctionne, y compris un navigateur sans tête.

Règle de cycle : un fichier repacké est un fichier sharethought. Même format, même marqueur, manifeste mis à jour.

Sécurité des délimiteurs

Les délimiteurs de sections sont <<<SECTION:nom>>> et <<<END:nom>>> sur leurs propres lignes. Si le contenu pourrait contenir une telle ligne, définissez "enc":"base64" dans le manifeste pour cette section et encodez le corps en base64 — pack le fait automatiquement lorsqu'un corps contient son propre délimiteur de fermeture ou la chaîne du marqueur.

Quelle collision pose réellement problème : un corps contenant les délimiteurs d'une autre section est inoffensif. Un corps contenant sa propre ligne <<<END:nom>>> est tronqué par une expression régulière non gourmande — tout ce qui suit cette ligne est orphelin. L'implémentation de référence détecte cela (erreurs de contenu orphelin et de délimiteur non équilibré) au lieu de tronquer silencieusement. En cas de doute, utilisez base64.

Maintenir la charge utile vivante

La charge utile ne fait pas partie des données de pixels du PNG, donc tout outil qui ré-encode l'image peut la supprimer silencieusement et laisser un PNG d'apparence normale sans profondeur. Cela inclut Image.open(f).save(g) dans Pillow, les éditeurs d'images et les optimiseurs de CDN. Repackez à partir des octets originaux plutôt que de ré-enregistrer le fichier rendu.

Les queues après IEND ne survivent qu'aux transports exacts octet par octet. Si le fichier doit traverser un pipeline d'images — une pièce jointe de chat, un générateur de vignettes, un transcodeur — utilisez le mécanisme de chunks (pack le fait) et téléchargez-le en tant que fichier plutôt que comme image en ligne afin que le PNG ne soit pas transcodé en JPEG.

Travailler avec un humain

L'humain voit le PNG — la couche visuelle simplifiée. Toute la profondeur (source SVG, doc, manifeste) est invisible pour une visualisation occasionnelle mais totalement présente pour vous. Lorsque l'humain pose une question sur l'image, dépaquetez et lisez le document pour le contexte. Lorsque l'humain souhaite que l'image soit modifiée, éditez le SVG et repackez visuellement. Lorsque l'humain souhaite ajouter de la profondeur, éditez le document et repackez textuellement. Le fichier est l'échange complet — un artefact, deux modalités, aucun canal secondaire.

Skills associés