Aller au contenu

Interface en ligne de commande (CLI)

Le CLI officiel de Gram (@gram-lang/cli) est l’outil de référence pour valider, compiler et gérer vos collections de recettes en local. Il fait office de passerelle entre vos fichiers .gram et le reste de votre stack technique (SSG, Next.js, apps mobiles, etc.).

npm install -g @gram-lang/cli

Le CLI tourne aussi bien sous Node.js (>=20.6.0, chargeant nativement les fichiers .env via process.loadEnvFile) que sous Bun. Aucune API spécifique à un runtime n’est requise, les deux options sont strictement équivalentes.

v1.2.0 Quand une nouvelle version est publiée sur npm, gram affiche une courte notice une fois la commande terminée (ignorée en CI et hors terminal interactif). Lancez gram upgrade pour vérifier et l’installer à la demande, ou désactivez cette notice avec gram config set updateCheck false — ou, pour une seule exécution, la variable d’environnement GRAM_NO_UPDATE_CHECK.

  • --verbose / --debug : fonctionne avec n’importe quelle commande, affiche la pile d’appels complète en cas d’erreur au lieu du message court.
  • --stock <specifiers> : traite les sous-recettes indiquées comme déjà prêtes ou en stock (ignore leurs étapes de cuisine sur la timeline et les regroupe en un produit unique sur la liste de courses). Accepte une liste de chemins séparés par des virgules (@bases/pate-sablee.gram,./sauces/bechamel.gram).

Initialise un nouvel environnement Gram dans le dossier courant.

  • Crée un dossier .gram/.
  • Génère un fichier config.yaml vierge (sans commentaires parasites).
  • Configure de manière interactive votre langue de recette préférée (actuellement en ou fr).
  • Configure de manière interactive votre fournisseur d’IA et modèle préférés.
  • Génère/met à jour un fichier .env pour vos clés d’API IA.
  • Génère une base de données d’ingrédients de départ très commentée à .gram/ingredients.yaml.
  • Génère un .gitignore pour éviter de versionner des clés sensibles.

Vérifie sur npm si une nouvelle version de @gram-lang/cli est disponible et propose de l’installer.

gram upgrade
  • Effectue toujours une vérification fraîche auprès du registre npm — indépendamment de la notice passive décrite ci-dessus.
  • Affiche une comparaison entre la version installée et la dernière disponible, puis demande confirmation avant de lancer npm install -g @gram-lang/cli@latest.
  • Ne fait rien (à part le rapport) si déjà à jour.

Valide vos fichiers .gram pour détecter les erreurs de syntaxe, l’intégrité structurelle, et les ingrédients non définis.

  • Exécute l’analyseur (parser) OhmJS pour attraper les erreurs de syntaxe.
  • Exécute le compilateur Kitchen pour débusquer les erreurs structurelles (ex : dépendances circulaires).
  • Se connecte à votre base ingredients.yaml pour épingler les ingrédients non documentés.
  • Les simples avertissements (estimations nutritionnelles, annotations incomplètes) ne font pas échouer la commande par défaut — seules les vraies erreurs structurelles (références introuvables, conflits de portée) déclenchent un exit 1. Passez --strict pour traiter la moindre alerte comme une erreur bloquante (parfait pour vos pipelines CI).
  • Options : --db <chemin>, --skip-db, --strict.

Compile vos recettes .gram au format JSON final minifié.

gram build "**/*.gram" --output ./dist
gram build brioche.gram --pretty
gram build brioche.gram --scale 2 --output ./dist-double
  • Par défaut, affiche le JSON pur directement sur la sortie standard (stdout) pour faciliter le piping.
  • Calcule automatiquement les données nutritionnelles et la standardisation des masses physiques via la base de données.
  • --scale <facteur> intègre l’ajustement des proportions (scaling) en dur dans le JSON de sortie. Le mode par référence (ex. farine=300g) n’est volontairement pas disponible ici : build traitant les fichiers par lots, un build reproductible ne doit pas dépendre de la lecture préalable d’une liste de courses spécifique. Utilisez gram view ou gram scale pour trouver votre facteur, puis passez ce multiplicateur numérique à build.
  • Lorsqu’un glob ou plusieurs fichiers sont passés en argument, les fichiers importés (via @use) par d’autres fichiers du même lot sont exclus par défaut, afin que leurs ingrédients ne soient pas comptés deux fois (isolément puis intégrés dans la recette hôte). Passez --include-modules pour les compiler malgré tout.
  • Options : --output/-o <dossier>, --pretty, --scale <facteur>, --db <chemin>, --skip-db, --include-modules.

Affiche une recette directement dans le terminal, proprement formatée dans une boîte ASCII.

gram view brioche.gram                      # Vue par défaut
gram view brioche.gram --scale 2            # Voir avec des quantités doublées
gram view brioche.gram --scale farine=300g  # Voir ajusté pour farine = 300g
  • Prend en charge la pagination automatique pour les recettes longues.
  • Affiche la nutrition calculée, les minutages, et les listes d’ingrédients à cocher.
  • Le flag --scale ajuste toutes les quantités d’ingrédients (liste de courses et références inline dans les étapes).
  • Options : --scale <facteur|réf>, --no-pager, --skip-db, --db, --bakers-math, --bakers-reference <id>, --bakers-math-only, --nutrition <base>.
  • --nutrition <auto|total|per-portion|per-100g> v1.1.0 choisit la base nutritionnelle affichée (par défaut auto : par portion quand la recette déclare portions:, sinon toute la recette). per-100g est rapporté pour 100 g du mélange brut assemblé — voir Estimation nutritionnelle.

Importe une recette depuis une page web, un fichier JSON-LD local ou une vidéo / Short YouTube, et la convertit en un fichier .gram à l’aide de l’IA.

gram import "https://exemple.com/recette"         # Page web avec Schema.org / JSON-LD
gram import recette.json                           # Fichier JSON / JSON-LD local
gram import "https://www.youtube.com/watch?v=…"    # Vidéo YouTube
gram import "https://www.youtube.com/shorts/…"     # YouTube Short (normalisé automatiquement)
gram import recette.json -o brioche.gram -y        # Écriture directe sans confirmation

Sources prises en charge :

  • URL de pages web : extrait automatiquement les métadonnées de recette structurées application/ld+json (Schema.org Recipe) présentes sur la page.
  • Fichiers JSON / JSON-LD locaux : lit directement les schémas JSON-LD enregistrés sur votre machine.
  • Vidéos et Shorts YouTube v1.1.0 : analyse directement la vidéo grâce aux capacités multimodales de Gemini (sans passer par des scrapers de sous-titres tiers).

Fonctionnement & Garde-fous :

  • Traduit et formate la recette en une syntaxe Gram valide, en respectant la configuration language.

  • Nécessite qu’une IA soit configurée (voir Configurer l’IA).

  • Avant d’écrire dans --output, la recette convertie est affichée pour relecture et confirmation. En contexte non interactif, la relecture est ignorée avec un avertissement — utilisez --yes/-y pour l’ignorer délibérément.

  • Rien n’est produit si l’import revient cassé. Deux contrôles portent sur le résultat et le refusent :

    • Contenu perdu — chaque @ingrédient écrit dans le fichier est confronté à ce que le compilateur a réellement enregistré. Un écart signifie que du texte a été avalé, le plus souvent par un commentaire // placé en milieu de phrase, qui masque la fin de sa ligne. Un tel fichier compile sans le moindre avertissement, en étant simplement amputé d’ingrédients.
    • Erreurs non résolues — l’IA bénéficie de deux tentatives de réparation pour les problèmes critiques. Si certaines subsistent, elles sont signalées plutôt qu’écrites.

    Passez --force pour forcer l’écriture du fichier malgré tout, ou réessayez avec un --model plus puissant.

  • source: et author: proviennent directement des données sources (ou des métadonnées YouTube), et sont supprimés s’ils manquent — jamais inventés par le modèle.

  • Les ingrédients retournés sans quantité sont comptés et listés par ligne. Ce n’est pas une erreur — @sel{} est idiomatique pour “au goût” — mais c’est le signal le plus direct du niveau de finition de la recette, pointant exactement les lignes à compléter. Un ingrédient quantifié une première fois puis simplement rappelé n’est pas recompté.

  • Lorsqu’une base de données d’ingrédients est active, l’analyseur signale aussi ce qu’il n’a pas pu résoudre (ingrédients inconnus, quantités sans masse exploitable). Ce sont des lacunes de votre base, non des échecs d’import, ils ne sont donc jamais bloquants.

Spécificités de l’import vidéo YouTube : v1.1.0

  • Google / Gemini uniquement : Gemini est le seul fournisseur traitant nativement les vidéos YouTube. Avec tout autre fournisseur, l’import s’arrête net.

  • Le coût augmente avec la durée, à raison d’environ 100 jetons d’entrée par seconde de vidéo : un Short compte quelques milliers de jetons, 10 minutes environ 60 000, 26 minutes environ 158 000. Les vidéos de plus de 20 minutes sont rejetées par défaut ; relevez le plafond via --max-duration <minutes>.

  • L’estimation des coûts requiert YOUTUBE_API_KEY : une clé standard de la YouTube Data API, lue uniquement dans l’environnement, jamais dans config.yaml. Sans elle, l’import tourne quand même, mais ne peut ni estimer la durée en amont ni appliquer la limite.

  • Attentes sur les quantités en vidéo : une vidéo énonce bien moins de quantités explicites qu’une recette rédigée. Le prompt ordonne au modèle de laisser {} vide plutôt que de deviner, et les contrôles ci-dessus refusent toute perte de contenu. Attendez-vous à compléter des manques à la main — c’est le résultat honnête, pas un échec.

  • title:, author: (nom de chaîne), et source: (URL) proviennent directement des métadonnées YouTube et sont repris tels quels.

  • Options : --output/-o <fichier>, --yes/-y, --force, --max-duration <minutes> v1.1.0, ainsi que les options IA communes.

Compile une liste de courses globale en agrégeant plusieurs recettes.

gram shop "**/*.gram"                 # Toutes les recettes
gram shop brioche.gram --scale 2      # Doubler toutes les quantités
gram shop "menus/*.gram" --scale 4    # Batch cooking × 4
  • Agrège intelligemment les quantités via la densité (volume → grammes quand la densité est connue).
  • Regroupe les ingrédients par leur champ category (famille culinaire : Fruits & Légumes, Produits Laitiers, Céréales, etc.).
  • Fusion par alias : si deux recettes utilisent des mots différents pour le même ingrédient (ex : butter et beurre), ils seront fusionnés sous leur clé canonique commune grâce aux alias de la base de données.
  • Les ingrédients sans quantité (ex : @sel{}) sont listés à côté de leur entrée principale plutôt que dans une section séparée.
  • Support complet des micro-unités : pincée, trait ou goutte s’agrègent sans broncher.
  • --scale <facteur> applique un multiplicateur numérique à toutes les recettes (uniquement facteur — le mode référence n’est pas disponible pour plusieurs fichiers).
  • Lorsqu’un glob ou plusieurs fichiers sont passés en argument, les fichiers importés (via @use) par d’autres fichiers du même lot sont exclus par défaut, afin d’éviter les doublons d’ingrédients sur la liste de courses globale. Passez --include-modules pour les inclure malgré tout.
  • Options : --format terminal|md|json, --output/-o <fichier>, --scale <facteur>, --db, --skip-db, --include-modules.

Lance un assistant de cuisine interactif étape par étape, directement dans le terminal.

gram cook brioche.gram
gram cook carbonara.gram --skip-db

Déroulement :

  1. Mise en place : liste complète et agrégée des ingrédients pour la recette (Espace pour commencer)

  2. Intro de section : ingrédients pour la section à venir (agrégés, montrés avant chaque section)

  3. Étapes de cuisson : une étape à la fois dans une disposition en deux colonnes : ingrédients à gauche, instructions à droite

  4. Écran de fin : temps total passé

Les ingrédients sont agrégés par section : les utilisations répétées du même ingrédient dans une section sont regroupées en une seule entrée avec les quantités jointes (ex : 200g beurre + 50g beurre → affiché comme 200g + 50g beurre, non additionné arithmétiquement). Le panneau des ingrédients montre toutes les quantités nécessaires pour cette section d’un seul coup d’œil.

Minuteurs — les minuteurs annotés dans la recette (~label{30min}) apparaissent dans la vue de l’étape :

  • Appuyez sur T pour lancer un minuteur ; s’il y en a plusieurs de disponibles, un sélecteur apparaît.
  • Les minuteurs s’exécutent en arrière-plan — ils restent visibles pendant que vous avancez dans les étapes.
  • Une cloche (terminal bell) retentit et l’affichage du minuteur bascule sur l’état statique « terminé » lorsqu’il tombe à zéro.
  • Q/Échap déclenchent le flux de confirmation de sortie plutôt que de rejeter un minuteur terminé.

Raccourcis clavier :

ToucheAction
Espace / EntréeÉtape suivante
BÉtape précédente
TLancer un minuteur
Q / ÉchapQuitter (demande confirmation si un minuteur est en cours)
  • Options : --scale <facteur|réf>, --skip-db, --db.

Affiche un diff sémantique de la recette (en comparant logiquement les ingrédients, minutages, sections et températures, plutôt qu’un bête diff de texte brut).

gram diff brioche.gram                        # Arbre de travail vs HEAD (plus courant)
gram diff brioche.gram --ref HEAD~2           # vs un commit git spécifique
gram diff brioche.gram --ref v1.2             # vs un tag git
gram diff brioche-v1.gram brioche-v2.gram     # Deux fichiers explicites (pas besoin de git)

Le diff couvre sept axes :

  • Ingrédients — quantités ajoutées/retirées/modifiées, avec le percentChange quand les unités correspondent.

  • MinutagestotalTime, cookTime, activeTime, preparationTime en minutes.

  • Sections — sections ajoutées/retirées et changements du nombre d’étapes.

  • Frontmatter — modifications sur portions, description, et d’autres champs de métadonnées. Un changement de title est suivi séparément et affiché sur sa propre ligne au-dessus du bloc frontmatter.

  • Préparations — modes de préparation modifiés par ingrédient (ex : “en dés” → “émincé”).

  • Températures & Minuteurs — cibles de température et durées de minuteur ajoutées/retirées/modifiées par section.

  • Modules — un import @use ajouté, retiré, relié à un nouveau nom, ou re-scalé avec un facteur différent.

  • Calcule le diff sur les objets compilés par le moteur Kitchen. Ainsi, un simple reformatage syntaxique (espaces, sauts de ligne) ne remontera aucun diff.

  • Le mode Git exige que le fichier soit suivi (tracked). Renvoie une erreur gracieuse si git est introuvable sur la machine.

  • Une section importée via @use ne compte jamais dans les axes Sections/Préparations/Températures/Minuteurs — sinon l’ajout d’un seul import ferait croire que toutes les autres sections ont bougé. C’est l’axe Modules qui rend compte de son propre import. gram diff ne résout pas @use de lui-même pour autant : chaque côté est compilé isolément, donc un changement à l’intérieur d’une base importée n’apparaît pas ici — pour ça, diffez directement le fichier de la base.

  • Options : --ref <réf-git>.

Affiche une comparaison avant/après des quantités d’ingrédients selon l’ajustement choisi.

gram scale brioche.gram --scale 2            # Doubler toutes les quantités
gram scale brioche.gram --scale 0.5          # Diviser par deux toutes les quantités
gram scale brioche.gram --scale farine=300g  # Ajuster les proportions pour farine = 300g
gram scale brioche.gram --scale oeufs=3      # Ajuster les proportions pour oeufs = 3
  • Affiche un tableau de comparaison : quantités d’origine (estompées) vs ajustées (en vert).
  • Les quantités qui ne s’ajustent pas (valeurs textuelles comme “1 pincée”) sont listées séparément.
  • Lève un warning pour les facteurs extrêmes (en-deçà de ×0.1 ou au-delà de ×20) et rappelle que les temps de cuisson, eux, ne sont jamais modifiés.
  • Le mode par référence (id=valeur) déduit le facteur à partir de la quantité actuelle de l’ingrédient ciblé (l’ID doit correspondre à sa clé). Les unités de la même famille sont converties à la volée (ex. farine=1kg pour une recette écrite en 500g). La passerelle masse ↔ volume (ex. eau=150g pour une recette en ml) fonctionne aussi, à condition qu’une densité soit connue (soit via gram db enrich, soit via une surcharge densities: ["eau:1.0"] dans le frontmatter).
  • Tous les ingrédients ne peuvent pas être une cible de référence : les ingrédients fixes (@=), les quantités relatives (70% @&farine), les ingrédients utilisés uniquement dans une sous-recette, les ingrédients dans un groupe d’alternatives, et les ingrédients divisés entre des unités incompatibles sont rejetés avec une erreur spécifique expliquant pourquoi. Voir la fiche Analyse approfondie : Ajustement des proportions pour la liste complète.
  • Options : --scale <facteur|réf>, --skip-db, --db.

Observe un dossier (watch) et relance automatiquement gram check à chaque sauvegarde de vos fichiers .gram.

gram watch                              # Observe la racine du projet
gram watch recettes/                    # Observe un dossier spécifique
gram watch --build --output ./dist      # Compile aussi les fichiers modifiés en JSON
  • Affiche une ligne de résultat horodatée par changement : [12:34:01] ✓ brioche.gram ou ✗ brioche.gram — 1 error.
  • Les erreurs s’affichent inline sous le nom du fichier. Le watcher ne crashe jamais suite à une erreur.
  • Un délai antirebond (debounce) de 150ms évite les exécutions redondantes.
  • Sauvegarder une base relance aussi le contrôle de chaque fichier du dossier observé qui l’importe via @use, directement ou transitivement — pas seulement le fichier modifié sur le disque. Une ligne → rechecking N dependent file(s) indique combien.
  • Options : --build, --output/-o <dossier>, --skip-db, --db.

Fouille votre projet pour trouver des recettes utilisant une combinaison précise d’ingrédients.

gram suggest --with "beurre, oeufs"
gram suggest --with "poulet" --without "crème"
gram suggest --with "citron, ail" --top 5 --min-match 50
gram suggest --with "butter" --json           # prise en compte des alias: butter → beurre
  • Parse tous les fichiers .gram en parallèle (sans aller jusqu’à la compilation complète). C’est extrêmement rapide, même sur un répertoire massif.
  • Correspondance tenant compte des alias : si une base de données est configurée, les noms d’ingrédients sont résolus via l’index d’alias. Chercher "butter" fera correspondre les recettes contenant @beurre.
  • Attribue un score à chaque recette par pourcentage de correspondance (correspondance / total avec-termes). Utilisez --min-match pour filtrer les résultats à faible score.
  • --without exclut immédiatement toute recette contenant un de ces ingrédients.
  • Options : --with/-w <csv>, --without <csv>, --top/-n <n> (défaut 10), --min-match <0–100> (défaut 1), --pattern <glob>, --db, --skip-db, --json.

Génère un HTML prêt pour l’impression et l’ouvre dans le navigateur par défaut.

gram print brioche.gram                   # Générer et ouvrir
gram print brioche.gram --scale 2         # Imprimer avec quantités doublées
gram print brioche.gram --no-open         # Ne pas ouvrir, imprimer le chemin sur stdout
  • Le HTML est écrit dans un fichier temporaire (gram_print_<horodatage>.html dans le dossier temporaire du système) et ouvert avec le navigateur par défaut (open sur macOS, xdg-open sur Linux, cmd /c start sur Windows).
  • Sortie identique à gram export --format html — idéal pour la boîte de dialogue d’impression du navigateur (Ctrl+P / Cmd+P) pour produire un PDF A4.
  • --no-step-qty — masque les quantités d’ingrédients dans le texte des étapes.
  • Options : --no-open, --scale <facteur|réf>, --no-step-qty, --skip-db, --db, --bakers-math, --bakers-reference <id>, --bakers-math-only, --nutrition <base>.
  • --nutrition <auto|total|per-portion|per-100g> v1.1.0 choisit la base nutritionnelle affichée (par défaut auto : par portion quand la recette déclare portions:, sinon toute la recette). per-100g est rapporté pour 100 g du mélange brut assemblé — voir Estimation nutritionnelle.

Exporte une recette vers Markdown ou HTML prêt à l’impression.

gram export brioche.gram --format md                   # brioche.md à côté de la source
gram export brioche.gram --format html -o ~/print.html # chemin de sortie explicite
gram export brioche.gram --format html --scale 2       # exporter aux quantités doublées
  • --format md — Markdown standard avec une liste de courses, l’équipement et les étapes numérotées.
  • --format html — Document HTML A4 autonome avec CSS intégré pour l’impression, avec icônes SVG Lucide (pas de dépendances externes). Il charge Courier Prime et Inter depuis Google Fonts, donc une connexion réseau est requise lors de la première ouverture.
  • Chemin de sortie par défaut : le même dossier que le fichier source, avec l’extension remplacée (.gram.md ou .html).
  • --no-step-qty — masque les quantités d’ingrédients dans le texte des étapes. Format HTML uniquement — sans effet avec --format md.
  • Options : --format md|html, --output <chemin>, --scale <facteur|réf>, --no-step-qty, --skip-db, --db, --bakers-math, --bakers-reference <id>, --bakers-math-only, --nutrition <base>.
  • --nutrition <auto|total|per-portion|per-100g> v1.1.0 choisit la base nutritionnelle affichée (par défaut auto : par portion quand la recette déclare portions:, sinon toute la recette). per-100g est rapporté pour 100 g du mélange brut assemblé — voir Estimation nutritionnelle.

Formate automatiquement les fichiers .gram (in-place) en appliquant 13 règles canoniques via le package @gram-lang/format.

gram format                              # Formater tous les fichiers *.gram
gram format brioche.gram                 # Formater un fichier unique
gram format "recettes/**/*.gram" --check # Vérif CI — exit 1 si un fichier a besoin d'être formaté

13 Règles canoniques appliquées :

  1. Formatage du frontmatter — normalisation des délimiteurs et nettoyage des espaces
  2. Titres d’en-tête de section — espace unique après ##
  3. Indexation des étapes — préfixes d’étape normalisés (1. )
  4. Blocs d’action — préfixes d’action normalisés ([Mélanger])
  5. Jetons d’ingrédients — espacement et syntaxe des crochets @ingrédient{qte}
  6. Jetons de matériel : espacement et syntaxe #matériel{qte}
  7. Jetons de minuterie — espacement ~minuterie{durée} et passif ~_minuterie{durée}
  8. Jetons de température — formatage ^temp{valeur}
  9. Déclarations et références d’intermédiaires — espacement ->&pâte et &pâte
  10. Syntaxe composite — syntaxe des ingrédients composites <@parent
  11. Zéros décimaux inutiles{500.0g}{500g}, {1.50g}{1.5g}
  12. Formatage des commentaires — espace propre après les sigles de commentaire (//)
  13. Nettoyage des espaces et saut de ligne final (EOF) unique

Sortie par fichier : ✔ brioche.gram 2 IDs lowercased · 1 trailing zero removed Le formatter est strictement idempotent : l’exécuter deux fois de suite ne modifiera jamais le code une seconde fois.

  • Options : --check.

Commandes imbriquées sous gram config pour lire et écrire la configuration projet (ou globale).

Affiche toutes les valeurs de configuration de la config du projet local et de la config globale.

gram config list

Affiche une seule valeur de configuration sur stdout.

gram config get ai.provider      # → google
gram config get database         # → .gram/ingredients.yaml
gram config get ai.provider --global

Définit une valeur de configuration.

gram config set database ./ma-bdd.yaml
gram config set ai.provider google
gram config set ai.apiKey AIza...           # Écrit GEMINI_API_KEY dans .env
gram config set database ./bdd-globale.yaml --global
  • Les valeurs sont écrites dans .gram/config.yaml par défaut ; utilisez --global pour ~/.config/gram/config.yaml.
  • Les clés sensibles (ai.apiKey) sont toujours écrites dans le fichier .env du projet. Le nom de la variable d’environnement est dérivé du fournisseur configuré (ex : googleGEMINI_API_KEY).
  • Les nombres et booléens sont coercés automatiquement ("2"2, "true"true).

Supprime une valeur de configuration.

gram config unset ai.model
gram config unset ai.apiKey         # Supprime la var d'environnement de la clé API du .env

Commandes imbriquées sous gram db pour gérer votre ingredients.yaml.

  1. Scanne vos recettes pour débusquer les ingrédients manquants et les injecter dans la base de données.

    • L’assistant interactif de fuzzy matching (distance de Levenshtein) vous aide à esquiver les doublons (fautes de frappe, pluriels).
    • Options : --dry-run/-n (aperçu sans écriture), --db <chemin>.
  2. Fait appel à l’IA pour traquer et résoudre les doublons sémantiques et les pluriels de la base de données.

    • Détecte les doublons inter-langues (ex : sucre / sugar) et les formes plurielles (ex : oeufsoeuf).
    • Pour chaque conflit, vous décidez de la clé à conserver. La clé écartée est automatiquement recyclée en alias.
    • Affiche un diff nutritionnel (uniquement les champs qui diffèrent) quand les deux entrées ont des données contradictoires, pour que vous puissiez faire un choix éclairé.
    • Options : --report/-r (afficher les problèmes sans appliquer les corrections), --db <chemin>, ainsi que les options IA communes.
  3. v1.1.0

    Lâche l’IA sur votre base de données pour proposer les données manquantes, puis vous fait passer en revue chaque proposition avant d’écrire quoi que ce soit.

    • Propose les champs density, unit_weight, nutrition, category, et tags par lots (unit_weight est rempli avec density). category est une famille culinaire (ex : Légumes, Produits Laitiers, Céréales) — distincte des tags libres.
    • category/tags sont écrits automatiquement ; density/unit_weight/nutrition passent par une revue interactive entrée par entrée (accepter, modifier ou passer), car ce sont des données propres à un produit précis — vous seul savez ce que vous avez réellement dans votre cuisine.
    • Une valeur acceptée telle quelle est taguée # [LLM] dans ingredients.yaml, pour repérer les estimations IA jamais relues par un humain.
    • Peut être relancée sans crainte : ne propose que les champs encore vides.
    • Options : --ingredient <slug> (enrichir une seule entrée), --field density|nutrition|tags|category|all (défaut all), --yes/-y (ignorer la revue et tout accepter automatiquement — pour un usage scripté), --report/-r (aperçu de ce qui a besoin d’être revu, sans rien écrire), --db <chemin>, ainsi que les options IA communes.
v1.1.0

Valide l’intégrité de votre ingredients.yaml.

  • Vérifie les erreurs de schéma, les alias dupliqués, et les valeurs incohérentes (ex : densité > 2.5).
  • Une entrée viciée ne fait pas crasher l’outil : chaque ligne est validée indépendamment. Le rapport final renverra la liste exhaustive des problèmes en une seule passe.
  • Options : --strict (exit 1 sur avertissements, utile en CI).

Cherche et affiche les entrées d’ingrédients en détail.

gram db search beurre            # Correspondance partielle sur id, nom, ou n'importe quel alias
gram db search --tag laitier     # Tous les ingrédients tagués 'laitier'
gram db search --category Grains # Tous les ingrédients dans la catégorie Grains
gram db search --missing nutrition  # Entrées n'ayant pas encore de données nutritionnelles
gram db search --exact beurre --count  # Imprimer seulement le nombre de correspondances
  • Options : --tag <tag>, --category/-c <category>, --missing nutrition|physical|aliases, --exact, --count, --json, --db <chemin>.

Fusionne une base de données d’ingrédients distante avec votre base locale.

gram db merge ~/shared-ingredients.yaml          # Résolution de conflit interactive
gram db merge community.yaml --prefer remote     # Utilise toujours les valeurs distantes en cas de conflit
gram db merge community.yaml --only-new          # Ajoute seulement les nouvelles entrées, passe la résolution de conflit

La fusion prend en compte les alias : si votre base a butter avec l’alias beurre, et que la source a une entrée beurre, ils sont reconnus comme étant le même ingrédient.

  • Options : --prefer local|remote (défaut local), --dry-run, --only-new, --db <chemin>.

Le CLI fusionne la configuration depuis ~/.config/gram/config.yaml (globale) et .gram/config.yaml (projet).

v1.1.0

Toutes les commandes qui appellent l’IA — gram import, gram db lint et gram db enrich — acceptent les mêmes trois options et affichent le modèle qu’elles s’apprêtent à appeler, pour qu’un choix inattendu soit visible avant de vous coûter quoi que ce soit :

gram import recette.json --model gemini-3.1-pro   # pour cette exécution seulement
gram db enrich --provider anthropic               # pour cette exécution seulement
gram db lint --pick-model                         # choisir de façon interactive
  • --model <nom> — utiliser un autre modèle pour cette exécution. Le fournisseur ne change pas.
  • --provider <google|openai|anthropic|ollama> — utiliser un autre fournisseur pour cette exécution. Le fournisseur n’est jamais deviné à partir du nom du modèle.
  • --pick-model — choisir fournisseur et modèle dans un menu. Nécessite un terminal ; dans un script, utilisez plutôt --provider/--model.

Aucune de ces options n’écrit dans votre configuration. Pour changer la valeur par défaut durablement, utilisez gram config set ai.model <nom> ou relancez gram init.

Quand --provider (ou --pick-model) désigne un fournisseur que votre configuration n’a pas nommé, les champs model, apiKey et baseUrl écrits sous ai: ne sont pas repris : ils appartiennent au fournisseur pour lequel ils ont été écrits. Gram réclamera la variable d’environnement propre au nouveau fournisseur. Cela vaut que votre bloc ai: désigne un autre fournisseur ou n’en désigne aucun : un bloc sans provider: appartient au fournisseur que Gram aurait auto-détecté, pas à celui que vous avez choisi à la main.

Gram utilise une hiérarchie de secours (fallback) en cascade pour les identifiants sensibles comme les clés API de l’IA :

  1. Variables d’Environnement : La variable du fournisseur utilisé — GEMINI_API_KEY, OPENAI_API_KEY ou ANTHROPIC_API_KEY (du système ou d’un .env) — a toujours le dernier mot. C’est l’approche recommandée pour sécuriser des secrets en local comme en CI/CD.
  2. Secours config.yaml : Si cette variable manque, Gram se rabat sur le champ ai.apiKey de votre config.yaml — mais uniquement s’il a été écrit pour le fournisseur appelé.

Une clé n’est jamais réutilisée d’un fournisseur à l’autre. Si vous avez configuré provider: openai et que seule GEMINI_API_KEY est exportée, Gram s’arrête et réclame OPENAI_API_KEY plutôt que d’envoyer votre clé Google à OpenAI. La même règle vaut entre les couches de configuration : un bloc ai: global est intégralement ignoré si votre projet choisit un autre fournisseur.

Voici la référence complète de tous les paramètres disponibles dans config.yaml :

version: 1                     # Réservé aux futures migrations de configuration — inutilisé pour le moment
database: ".gram/ingredients.yaml" # Chemin relatif ou absolu vers la base de données
language: "fr"                 # Langue pour tout le contenu généré par l'IA (catégories, tags, recettes importées)
                               # Supporté : en, fr, de, es, it, pt, nl, ja, zh — défaut : en
updateCheck: true             # Passez à false pour désactiver la notice de mise à jour affichée après chaque commande

# Alias nommés pour les imports `@use` — `@use "@bases/pate.gram"` résout vers
# "<racine du projet>/partage/bases/pate.gram". Voir la référence des imports de modules.
paths:                         # Ajouté en v1.2.0 pour les alias de modules
  bases: "./partage/bases"

# Paramètres IA pour `gram import`, `gram db enrich` et `gram db lint`
ai:
  # Fournisseurs supportés : "google", "openai", "anthropic", "ollama"
  provider: "google"

  # Chaîne de modèle spécifique (défaut 'gemini-3.5-flash' pour google,
  # 'gpt-4.1-nano' pour openai, 'claude-haiku-4-5-20251001' pour anthropic, 'llama4' pour ollama)
  model: "gemini-3.5-flash"

  # Clé API. Préférez la variable ENV du fournisseur ci-dessus : GEMINI_API_KEY (google),
  # OPENAI_API_KEY (openai), ANTHROPIC_API_KEY (anthropic). Ce champ n'est lu que pour le
  # fournisseur déclaré ci-dessus — il est ignoré sous --provider ou dans un projet qui en
  # choisit un autre. Le fournisseur "ollama" ne lit aucune clé API.
  # ATTENTION : Préférez utiliser un fichier .env au lieu de versionner ce fichier avec votre clé.
  apiKey: "VOTRE_CLE_API"

  # URL de base personnalisée — uniquement lue pour le fournisseur "ollama".
  # Vaut "http://localhost:11434/v1" (ou la var ENV OLLAMA_BASE_URL) par défaut.
  baseUrl: "http://localhost:11434/v1"