Changelog
1.2.0 - 2026-08-22
Section titled “1.2.0 - 2026-08-22”New features
Section titled “New features”- Parser & Kitchen: Introduced modular recipes and multi-file imports via the
@usedirective:- Import external base recipes directly into a recipe using
@use "./bases/shortcrust-pastry.gram" as &shortcrust. - Imported steps seamlessly interleave into the global ALAP scheduling timeline, preserving resting times and dependencies.
- Automatically scales imported base quantities when referenced with specific yields (e.g.
&shortcrust{250g}halves a 500 g base recipe). - Supports destructured multi-yield imports (e.g.
@use "./bases/tart-elements.gram" as { &crust, &frangipane }) scaling each component independently. - Supports multi-word bindings using bracket notation (e.g.
@use "..." as &pastry dough{}). - Resolves project-root paths (
@/bases/...) and custom path aliases defined underpaths:in.gram/config.yaml. - Added
--stockCLI flag to treat pre-made base imports as stock items (zero timeline overhead, single shopping list line, retaining full nutritional totals). - Supports retro-planning timeline offsets directly on
@useimport lines (e.g.@use "./bases/levain-starter.gram" as &starter ~{-2d}). - Spliced sections in HTML, Markdown, and print outputs display origin badges crediting their source module.
gram diffandgram watchnow track import additions, removals, rebinding, rescaling, and dependency file changes.- Language Server and VS Code extension support live dependency composition, Go to Definition into base files, path auto-completion, and quick-fix diagnostics for missing exports.
- Exported
createMemoryHostfrom@gram-lang/modulesfor browser and in-memory module graph resolution. - Corrected whole-recipe mass and nutrition totals to accurately include spliced intermediate (
-> &) masses. - Normalized rescaled ingredient quantities to one decimal place for display consistency across all outputs.
- Import external base recipes directly into a recipe using
- CLI: Added automatic update checking and a new
gram upgradecommand:- gram now checks npm in the background and prints a short “update available” notice after a command finishes (skipped in CI and non-interactive runs).
- Added
gram upgradeto check for and install the latest version on demand — it always performs a fresh check and asks for confirmation before running the install. - Added an
updateCheck: falsesetting inconfig.yaml(or theGRAM_NO_UPDATE_CHECKenvironment variable for a single run) to opt out of the passive notice.
- Parser & Kitchen: Composite ingredients written with a short, generic child name (like
@juice<@lemon) are now protected against a silent ingredient-database mix-up:- The compiler now warns when the same short composite name (e.g. “juice”) is drawn from two different parents within one recipe (lemon in one step, orange in another) — until now this went unnoticed and the two usages silently shared one entry in the ingredient database.
gram db syncreports the same conflict across your whole recipe collection, so two unrelated recipes that happen to use the same generic composite name don’t overwrite each other’s nutrition and density data.- The AI recipe importer (
gram import) now writes composite ingredients with their full name (e.g. “lemon juice”) instead of a short generic one, so newly imported recipes don’t create this conflict in the first place.
Bug fixes and improvements
Section titled “Bug fixes and improvements”- Parser & Kitchen: Fixed
gram scale(and any other caller that re-scales an already-compiled recipe) silently leaving alternative ingredient groups (@butter|@margarine) and composite parent-draw quantities (@egg-yolks{2}<@eggs{3}) unscaled. - Parser & Kitchen: Improved clarity and tone for diagnostic messages and revised severity tiers:
- Reworded compiler and module diagnostic messages to provide actionable guidance and avoid alarmist phrasing for standard culinary approximations.
- Reclassified non-critical notices (
MISSING_MACROS,UNKNOWN_MASS,TRACK_CONTENTION) toinfoseverity so they do not clutter warning counters. - Enhanced Playground and editor diagnostic styling with dedicated color schemes for errors (red), warnings (amber), and notices (blue).
- Language Server: Normalized error states and unified diagnostic handling across Playground and VSCode extensions:
- Unified Playground diagnostics into a centralized full-width debug console with filter pills and interactive jump-to-location navigation.
- Added mobile segmented tab navigation (
[Editor]/[Preview]) and responsive single-row toolbar layout for mobile viewports. - Resolved nutrition diagnostics disconnection in the analyzer by merging physical and nutritional warnings into primary compiler warnings — the editor only turns these into squiggles when they carry a real position, so an incomplete-nutrition notice no longer misplaces itself at the top of the file.
- Markdown and print HTML exports keep their explicit “incomplete data” note next to the affected nutrition figures; the interactive HTML preview conveys the same gap through its coverage badge instead.
- A scale-target that fails to resolve (e.g. an unconvertible unit) now correctly turns the Playground’s status badge and error panel red instead of being undersold as a warning, without marking the file tab as if the recipe itself had a syntax error.
- The Playground diagnostics console no longer gets stuck filtered on a category that just emptied out (e.g. after fixing the warning it was showing).
- Resolved silent compilation failures in the Language Server by capturing pipeline exceptions and pushing actionable error notifications to webviews — including when the thrown error carries no message, which previously slipped past the check silently.
- A compile-time failure (parses fine, but
compile()/analyze()throws) now also surfaces as an editor diagnostic, not only as a webview notification, so it’s still visible with the preview panel closed. - Added bidirectional webview messaging in the VSCode extension to jump directly to error offsets in the active editor.
- Localized the Playground diagnostics console’s collapse/expand tooltip, previously hardcoded in French regardless of site locale.
- Parser & Kitchen: Fixed a false circular reference warning when multiple sections used percentage-of formulas on same-named ingredients by scoping cycle detection per section.
1.1.0 - 2026-08-16
Section titled “1.1.0 - 2026-08-16”New features
Section titled “New features”- CLI / Import: Support importing recipes directly from YouTube videos and Shorts via
gram import:- Uses Gemini multimodal video understanding to analyze cooking videos and generate structured
.gramrecipes. - Automatically populates
title:,author:(channel name), andsource:metadata from YouTube. - Automatically normalizes YouTube Shorts URLs to standard video formats.
- Added
--max-duration(default 20 minutes) to prevent accidental token overconsumption on long videos. - Displays upfront video duration and estimated token cost when
YOUTUBE_API_KEYis configured.
- Uses Gemini multimodal video understanding to analyze cooking videos and generate structured
- CLI / AI: Explicit AI model and provider selection for all AI commands (
gram import,gram db lint,gram db enrich):- Added
--model,--provider, and--pick-modelCLI flags to inspect or override the active model per run without altering persistent config. - The CLI now displays the active AI provider, model, and configuration source before execution.
- Isolated provider API keys so credentials configured for a specific provider are never leaked or reused when switching providers.
- Fixed
gram importhanging in non-interactive environments when--outputwas used without--yes.
- Added
- CLI / Database: Added physical coherence and sanity checks across
gram db validateandgram db enrich:gram db validatechecks calorie consistency against Atwater estimates (4 kcal/g protein/carbs, 9 kcal/g fat, 7 kcal/g alcohol).gram db validatechecks sub-macro coherence (sugars exceeding total carbs, fat sub-types exceeding total fat).gram db validateenforces category-based density sanity ranges and unit weight bounds.gram db enrichconstrains AI estimates within physical limits and prompts self-verification of Atwater consistency.gram db enrichpasses known ingredient categories to the AI to reduce guesswork.
- CLI / Database:
gram db enrichnow walks you through an interactive review before writing AI estimates toingredients.yaml:- Preview, accept, edit, or skip AI-proposed density, unit weight, and nutrition values per ingredient.
- Unedited AI values are tagged with
# [LLM]iningredients.yamlto track provenance. - Added
--report(-r) to preview needed database enrichments without writing changes, matchinggram db lint --report(replaces--dry-run/-n). - Added
--yes(-y) to accept all estimates automatically for non-interactive scripting. - Automatically falls back to accepting values with a warning when executed in non-interactive (non-TTY) environments.
- CLI / Import: Added integrity guardrails and diagnostic reporting to
gram import:- Validates compiled AST tokens against AI output to detect lost ingredients or truncated steps, aborting instead of writing corrupted files (overridable with
--force). - Reports uncorrected AI generation errors with actionable diagnostics.
- Identifies imported ingredients missing quantities and lists their line numbers for easy manual completion.
- Reports unknown database ingredients and unweighed units when an ingredient database is present.
- Prevented hallucinated placeholder metadata in
author:andsource:fields. - Redirected progress indicators to
stderrso stdout redirection (gram import ... > recipe.gram) produces clean recipe files.
- Validates compiled AST tokens against AI output to detect lost ingredients or truncated steps, aborting instead of writing corrupted files (overridable with
- Nutrition & Rendering: Support flexible nutrition serving bases (per portion and per 100 g) alongside whole-recipe totals:
- Declaring
portions:in recipe frontmatter now calculates per-portion nutrition and keeps values constant when scaling recipes. - Added a standardized
per-100gnutrition basis calculated from raw assembled recipe mass. - Added the
--nutrition <auto|total|per-portion|per-100g>option togram view,gram export, andgram print. - Added nutrition basis toggles in the web playground and VS Code live preview.
- Localized all nutrient names and table headers across supported languages (French and English).
- Declaring
Bug fixes and improvements
Section titled “Bug fixes and improvements”- Parser & Kitchen: Fixed an issue where multi-word composite ingredients marked with
&(e.g.@juice{1}<@&unwaxed lemon{}) were duplicated in the shopping list instead of being combined into a single purchase item. - Analyzer & Language Server: Improved nutrient calculation completeness and editor hover precision:
- Saturated, monounsaturated, polyunsaturated fats, and alcohol are now included in recipe nutrition totals instead of being omitted.
gram db enrichcan now propose monounsaturated and polyunsaturated fat values during ingredient enrichment.- Fixed the editor hover tooltip incorrectly displaying sodium in grams instead of milligrams.
- Analyzer: Fixed recipe total mass and section weights to include ingredients defined as relative percentages of another ingredient (e.g.
@water{60% @&flour}).
1.0.1 - 2026-08-07
Section titled “1.0.1 - 2026-08-07”Bug fixes and improvements
Section titled “Bug fixes and improvements”- Fixed the
--helpdescription ofgram checkto clarify that it also checks database completeness by default, unless--skip-dbis used. - Reduced the AI provider costs of running
gram import:- Prompt caching is now enabled, so retries reuse the cached prompt instead of paying full price.
- The command now sends a minimized recipe payload instead of the full raw webpage data.
- The self-correction loop no longer spends retries on informational warnings (like missing database ingredients).
- Formatting is now handled locally instead of spending an AI call to fix it.
- Improved the quality and correctness of recipes generated by
gram import:- Fixed an issue where English words would leak into non-English recipes.
- Fixed grammatical errors (dropped articles) around ingredient and cookware references in non-English output.
- Descriptive adjectives (like “boneless skinless”) are now correctly extracted as preparation steps instead of creating unwieldy ingredient names.
- Corrected the generated syntax for multi-word cookware references.
- Fixed an issue where the target language was sometimes incorrectly inferred.
- Improved decoding of accented characters and punctuation from the source recipe.
- Improved the CLI guidance printed after running
gram import:- It now correctly points to
gram db syncas the recommended next step. - It accurately reports any remaining compiler warnings (e.g., missing ingredients) instead of silently discarding them.
- It now correctly points to
- Fixed
gram initto ensure the interactive AI provider setup accurately offers the currently recommended models (e.g., llama4).
1.0.0 - 2026-08-06
Section titled “1.0.0 - 2026-08-06”Breaking changes
Section titled “Breaking changes”- Gram v1.0.0 Official Launch! 🎉 This milestone release marks the official 1.0.0 launch of Gram:
- Brand-New Website & Documentation: Rebuilt using Astro and Starlight with refined guides and an integrated blog.
- Refreshed Visual Identity: Brand-new logo.
- Infrastructure & Stability: Monorepo stability, test coverage, and migration to new Forgejo (
git.gram-lang.org) mirrored to GitHub and Codeberg.
[1.0.0-beta.5] - 7/26/2026
Section titled “[1.0.0-beta.5] - 7/26/2026”New features
Section titled “New features”- Introduce
@gram-lang/format, a standalone canonical code formatting engine for.gramfiles with 13 deterministic rules, used by the CLI and Language Server. convertUnit,standardizeMass, andanalyze()now accept an optionallangparameter/option, so unit names are resolved against the recipe’s own language when there’s an ambiguity between languages, instead of always falling back to a single global guess.UNIT_CONVERSIONS(the mass/volume conversion table) has moved to@gram-lang/i18n— if you imported it from@gram-lang/analyzer, import it from@gram-lang/i18ninstead.- The
language:setting in.gram/config.yamlnow also affects unit conversion and the shopping list’s category order (e.g. a database with French category names like “Légumes” now sorts correctly whenlanguage: "fr"is set) — previously it only affected AI-generated content. - Fixed two bugs in the recipe formatter:
- A
#cookwarereference at the very start of a line (e.g.#pan(20cm)) was incorrectly treated as a malformed section header and got a space inserted after the#, turning it into plain text and silently breaking the recipe. - Blank-line spacing before a
## Sectionheader was being promoted to two blank lines instead of Gram’s actual convention of exactly one blank line between every step and/or section. The formatter now consistently enforces a single blank line everywhere, including before section headers. As a result, thesectionSpacingcounter (previously reported alongsideconsecutiveBlankLines) has been removed — this kind of spacing fix is now reported as part ofconsecutiveBlankLines.
- A
@gram-lang/i18nnow also owns the numeric conversion factors for units (grams, milliliters, etc.) and time (minutes, hours, etc.), exported asUNIT_CONVERSIONSandTIME_TO_MINUTES— previously split across@gram-lang/analyzerand hardcoded inside@gram-lang/kitchen. No behavior changes; this just puts everything about units and time in one place.- Food categories now have stable, language-independent keys (
CATEGORY_KEYS,CategoryKey,getCategoryLabels,isCategoryKey) instead of only translated display labels. This meansgram db enrichnow stores a category identity that stays consistent regardless of your configured language, rather than a French or English label baked in at write time. - The Markdown, HTML, and printable/PDF outputs now show the same information consistently:
- Markdown recipes now include a nutrition summary (calories, carbs, protein, fat, etc.), which was previously only shown in HTML and print.
- Recipe comments now appear as numbered footnotes in Markdown and print output too, instead of only in HTML.
- When a shopping-list ingredient’s purchasing weight differs from what actually goes in the bowl (e.g. “1 avocado” vs. its usable flesh), Markdown and print now show the extra “gross weight” note, matching HTML.
- When the same ingredient appears in incompatible units that can’t be combined (e.g. “100g” and “1 cup” with no density available), Markdown and print now group them together with a “mixed units” warning, matching HTML.
gram formatand the editor’s “format on save” now share the exact same formatting rules, so a recipe formatted by one always looks identical when opened in the other. This adds a few new automatic cleanups to both: normalizing spacing around composite ingredients (@a{} < @b{}→@a{}<@b{}), tidying up intermediate-result declarations (->&name {}→->&name{}), making sure section headers have exactly one space after the#s, and converting tabs to spaces.- Add an interactive Gantt Chart view to the VS Code extension and export reusable Gantt chart rendering helpers (
toGanttHTML,attachGanttInteractivity) from@gram-lang/renderer. Provides a real-time timeline visualization of recipe steps, background timers, and target serving times directly in your editor and the web playground.
Bug fixes and improvements
Section titled “Bug fixes and improvements”- Fixed a bug where the wrong API key could be sent to the wrong AI provider. If you had, say,
GEMINI_API_KEYset in your environment but configuredprovider: openaiin.gram/config.yaml, your Gemini key could be sent to OpenAI instead. Each provider now only ever uses its own key. - Baker’s Percentage is now shown on an ingredient’s inline mention within step text, not just in the recipe’s ingredient list and shopping list — a dead type check meant this data was computed but never actually reached the inline token.
- The ingredient database schema no longer requires
densityon every entry that has aphysicalblock — an ingredient described only byunit_weight(e.g. “1 avocado”) is valid, matching the analyzer’s own documented example. Previously,gram db enrichcould write entries thatgram build/gram db validatewould then reject. gram diff(anddiffRecipes) now detects quantity changes inside composite ingredients (<@parent) and alternative groups (@a|@b), which it previously ignored entirely — a recipe that doubled a composite or alternative’s quantities used to be reported as having no changes at all. It also no longer loses one of two same-titled sections, or two same-named timers/temperatures within a single section, when comparing two recipes.- Fixed the same ingredient sometimes getting two different
normalizedMassvalues in one analyzed recipe (e.g.250.78328000000002in a recipe section vs.250.78in the shopping list) — the section-level and composite-child mass calculations now go through the same rounding as the shopping list, instead of duplicating the sequence without it. - Removed all uses of
anyfrom the physical/nutrition analysis layer’s internal types, typing them against the real shapes already exported by@gram-lang/kitchenand this package’s ownAnalyzedUsage/NutritionItem. No behavior change for this package beyond the fixes already released separately (see the relative-quantity type changeset). The renderer’s printable/PDF view gained one internalanyto stay compatible with the analyzer’s now-stricter shopping-list type — purely a type-level adjustment, no output change. - A relative-quantity ingredient’s resolved quantity (e.g.
@water{50% @&flour}) is now tagged with the correct"single"type instead of a value that didn’t match any of the documented quantity shapes — it happened to still render correctly today only because of an unrelated fallback, so this is a safety fix rather than a visible change. - Fixed a major parsing bug where a bare
@ingredientor#cookwaremention (one written without its own{}) would silently swallow an unrelated timer, temperature, cookware, or ingredient that appeared later on the same line into its own name, if that later element happened to close with a valid quantity — losing that element from the recipe entirely. For example,Brown the @&chicken for ~{3min}.used to lose the timer completely, turning it into a single ingredient named “chicken for ~”. Both the ingredient and cookware name now stop at the same sigils (@,#,~,^,&) that a bare reference (&name) already correctly stopped at. - Warnings about invalid ingredient database entries now go to stderr instead of stdout, so commands like
gram build recipe.gram | jqno longer break when the database has a bad entry. gram shopnow groups the shopping list by the same category names used everywhere else in the project (e.g. “Seafood” instead of “Fish”, “Vegetables” instead of “Produce”) instead of its own inconsistent list.- Fixed
gram formatrewriting values inside the recipe’s frontmatter (the---metadata block at the top of a.gramfile). For example, an email address likeJean@Example.cominauthor:used to get incorrectly lowercased toJean@example.com. Frontmatter is no longer touched by formatting rules meant for the recipe body. File writes are also now atomic and lock-protected, matching the rest of the CLI. - Fixed several French unit words silently resolving to the wrong physical quantity:
quart/quartsno longer resolves to the US liquid quart (946 mL) in French recipes — it’s a false friend (“un quart d’heure” means “a quarter of an hour”, not a unit of volume). It’s now reported as an unknown unit instead of silently misinterpreted. The English wordquart/quartsstill works in English recipes; only the spelled-out alias was removed,qtstill works everywhere.livrenow converts using the French “livre métrique” (500 g) instead of being silently treated as the imperial pound (453.592 g) — a ~10% error.tassenow converts using the French cup (250 mL) instead of being silently treated as the US cup (236.588 mL) — a ~6% error.pinteno longer resolves at all: the historical French pinte has no single reliable modern value, so an explicit “unknown unit” is better than a confident-looking wrong number.gallon/gallonsnow resolve correctly (previously only the abbreviationgalworked).
- The compiler now detects indirect circular dependencies between section intermediates (e.g. section A’s result depends on section B’s, whose result depends back on section A’s) and reports a
CIRCULAR_REFERENCEwarning for each one — previously only a direct self-reference was caught. - Removed all uses of
anyfrom the compiler’s internal types, replacing them with the real shapes already exported by the package (Usage,CompositeItem,StepToken, etc.), and cleaned up dead code paths in shopping-list aggregation. - Scaling a recipe by an extreme factor now fails with a clear error instead of silently producing
Infinity(serialized asnullin the output JSON). Scaled quantities are also rounded consistently, including a composite ingredient’s total, so results no longer show float noise like110.00000000000001. - Scaling a fraction quantity (e.g.
1/2 cup) no longer produces a malformed value in the compiled JSON ({"type": "fraction", "value": 1}with no numerator/denominator) — it’s now correctly represented as a plain numeric quantity. - Fixed several compiler correctness bugs:
- An empty section (or one containing only a comment) between two sections with steps used to break ALAP scheduling: the step right after the gap could get scheduled to overlap a timer still running from before the gap. It now correctly chains past the gap.
applyScale()(used bygram scaleand the playground) now scales cookware quantities. Previously it only scaled cookware “by accident” when called from insidecompile({ scaleFactor }), soapplyScale(compile(recipe), factor)silently left cookware unscaled.- A timer with an unrecognized time unit (e.g.
~{3 bananas}) or a missing unit no longer silently contributes a fabricated duration to the recipe’s total/active time. Both now raise a warning instead, matching how temperatures and retro-planning already behave. - Timer units are now displayed consistently (e.g. always “min”, never a mix of “min”/“mins”/“minutes” for the same physical duration in the same recipe).
- Fixed a spurious
TRACK_CONTENTIONwarning on the ordinary, intended use of named passive tracks (~_name{...}) — chaining several steps on the same physical resource (an oven, a fridge shelf) back-to-back no longer warns just because nothing else linked those steps together. The scheduler now understands that shared-track ordering the same way it already understands&intermediatedependencies, so the warning only fires when a real, unforeseen conflict remains. - Fixed several ways the language server could crash the whole editor session instead of just failing gracefully: a malformed entry in
ingredients.yaml(e.g. missingname) no longer crashes the server, opening a project in a virtual or remote workspace no longer crashes initialization, and a failed background database reload can no longer take down the process. Formatting, rename, and code actions also now always operate on the current document content instead of a possibly-stale cached version. ASTNodeis now a fully exhaustive, discriminated union that matches what the parser actually produces (CompositeAST,QuantityAST,TextQuantityAST, andRelativeQuantityASTwere previously missing from it), and composite ingredients (<@parent) now carry source location info like every other node. Making these types honest surfaced and fixed three real bugs in the language server: outline (document symbols), syntax highlighting (semantic tokens), and go-to-definition/rename/hover (reference and intermediate lookups) could silently miss or crash on content that isn’t wrapped in a## Sectionheader — a recipe with no headers at all, or with a comment before the first header.- Fixed two silent quantity-corruption bugs in fraction parsing: a decimal numerator (e.g.
1.5/2) used to be truncated to an integer before dividing, silently turning1.5/2into0.5instead of0.75; a zero denominator (e.g.1/0) used to produceInfinity, which serializes tonullin JSON, instead of being rejected outright. - The
hideStepQtyrendering option (hides ingredient quantities from step text) now works withtoHTMLandtoMarkdown, not justtoPrintHTML— it was previously silently ignored by the other two. - Fixed an HTML injection vulnerability (XSS) in
toHTML’s timing-card tooltips: a section title or named timer containing HTML would render unescaped in the “Active Time” / “Total Time” tooltips, exploitable via the playground or the VS Code preview. Both are now properly escaped like every other text field. toHTML,toMarkdown, andtoPrintHTMLnow type theirdataparameter instead of acceptingany. Typing it surfaced two real bugs, now fixed: the HTML nutrition panel’s “incomplete data” warning was never actually shown (it read a field that never existed, always showing a blank 0-calorie panel instead), and the printable/PDF view displayed sodium in grams instead of milligrams (1000x too high).- Fixed syntax highlighting (TextMate grammar): a bare
@ingredient,#cookware, or<@parentmention with no{}of its own would have its highlighted span incorrectly extend all the way to the next unrelated{...}on the line (e.g. a later&reference{}), coloring everything in between as if it were part of the same name. The name-matching patterns now stop at@,#,~,^, and&, the same sigils the compiler itself stops at. toMarkdownnow neutralizes raw HTML (<and&) in recipe titles, ingredient names, and step text. Previously, a recipe containing something like<img onerror=...>in its title would pass through untouched, which could run as a script if that Markdown was later converted to HTML — this is now escaped automatically, closing that gap for imported or shared recipes.gram import <url>now refuses to fetch addresses that aren’t publicly routable (localhost, private networks, link-local/cloud metadata addresses), including on redirects — closing a way for a malicious or compromised page to make the CLI fetch internal network resources. API keys stored viagram init/gram config setare now actually picked up when running the CLI under Node (previously only worked when run via Bun).gram db enrichnow clearly reports when nothing was written to disk instead of claiming success; AI-suggested ingredient values that are physically implausible (like an ingredient density far outside any real food) are now rejected instead of being written to your ingredient database.QuantityValueAST(the parser’s internal representation of a parsed number/fraction/range) is now a proper discriminated union instead of a flat interface with every field optional. This is an internal type-safety improvement with no behavior change — it’s what would have caught, at compile time, a real bug fixed earlier indiffRecipes(checkingqty.from/qty.to, fields that never existed on any variant).
[1.0.0-beta.4] - 7/23/2026
Section titled “[1.0.0-beta.4] - 7/23/2026”Breaking changes
Section titled “Breaking changes”- Section retro-planning (e.g.
## Section ~{-2h}) now enforces a strict signed-duration syntax instead of accepting arbitrary free text, preventing invalid timeline calculations. Added support for thed(day) time unit.
New features
Section titled “New features”- Added support for ALAP (As Late As Possible) scheduling. Passive timers and their dependencies are now natively pushed backwards from the end of the recipe, ensuring ingredients are prepared just-in-time rather than sitting idle on the counter. Also introduces two new compiler warnings for timeline conflicts:
TIME_PARADOXandTRACK_CONTENTION. - Exported the
runPipelinefunction alongside its associated types from@gram-lang/clito facilitate library and programmatic usage of the core compiler orchestration. - Renamed the
cookTimemetric toidleTimeacross the ecosystem to better reflect hands-off wait time. Additionally, passive timers sharing the same name are now automatically sequenced one after another on the same background track. - Added
llms.txtandllms-full.txtto the documentation site, providing a curated index and full concatenated specification tailored for AI assistants and agents.
Bug fixes and improvements
Section titled “Bug fixes and improvements”- Fixed the display of alternative ingredient and cookware groups (
@egg|@tofu). They now correctly render inline as a single joined line in shopping and section lists, rather than being dropped or rendered as oddly-wrapped sub-lists. - Fixed mass standardization silently failing for alternative ingredient groups. Mass and estimate metrics are now properly computed for each option independently, fixing missing totals in the shopping list.
- Added support for bare single-word children in composite ingredients (e.g.,
@juice<@lemon), and allowed independent preparation instructions on the parent side. Fixed section ingredient lists silently dropping the parent reference. - Fixed bare ingredient names incorrectly absorbing trailing punctuation (like periods). Also fixed multi-word unbraced names breaking alternative group parsing. (An orphan
|in step text is now correctly flagged as a parse error). - Fixed a bug where scaled fractions (e.g., when doubling a recipe) would incorrectly display their original, unscaled text values in section ingredient lists instead of the correctly multiplied amount.
- Fixed an issue where scaled fractions resulting in values below 1 (e.g.,
0.5) were rendered as raw decimals instead of common fractions (e.g.,1/2) in the section ingredient list. - Updated the AI generation prompt to accurately reflect the latest language syntax, strict retro-planning rules, and active/passive timer terminology.
- Fixed shopping list ingredient names defaulting to the database’s canonical wording when the recipe used a valid alias. The lists now correctly preserve the recipe’s original wording or translated alias, ensuring consistent language throughout.
- Fixed a parser crash that occurred when a section header named the section (
->&name) before defining its retro-planning (~{-2h}). - Fixed section and mise-en-place ingredient lists displaying raw database slug IDs (e.g.,
oeufs) instead of their correct, localized display names (e.g.,œufs). - Added detailed per-contribution time breakdowns (Active, Prep, Total) to compiled recipe metrics, and surfaced them as explanatory tooltips on the time summary badges in the HTML renderer.
[1.0.0-beta.3] - 7/14/2026
Section titled “[1.0.0-beta.3] - 7/14/2026”Bug fixes and improvements
Section titled “Bug fixes and improvements”gram checknow resolves syntax error line numbers from the parser’s structuredGramParseError.offsetinstead of regexing “line N” out of ohm’s prose message — line numbers are now always correct, not just when they happened to match that pattern.- Downgrade minimum VS Code version requirement to 1.75.0
- Updated deployment configuration to automate npmjs packages publication
- Updated docs for clarity and accuracy.
[1.0.0-beta.2] - 7/13/2026
Section titled “[1.0.0-beta.2] - 7/13/2026”Breaking changes
Section titled “Breaking changes”- fix!: load valid ingredients even when the database has a bad entry, and fix five correctness bugs
- fix!:
gram checkonly fails on structural errors by default (use--strictfor the old behavior), fix LSP completion race, and add ingredients.yaml live reload - feat!: validate and normalize temperature units, add a shared warning severity map, and fix accented/non-Latin ingredient slugs
- feat!: replace the
°temperature sigil with^, the~&passive timer marker with~_, and add mixed/Unicode fraction support
New features
Section titled “New features”- Added a bilingual (EN/FR) “API Reference” section covering the programmatic API of
@gram-lang/parser,@gram-lang/kitchen,@gram-lang/analyzer,@gram-lang/renderer, and@gram-lang/i18n— function signatures, options, JSON data formats, and the full warning-code catalogue. Reference tables (warning codes, AST node types, unit conversions, categories) are generated at build time directly from each package’s source, so they can’t drift out of sync with the code. Also fixed an incorrectanalyze()call example inhow-to/build-custom-ui.md. - Export
fetchRecipeandvalidateGramfrom the recipe-import service, enabling direct testing and reuse of the import validation pipeline. - Add
round2(value: number): number, exported from@gram-lang/kitchen. It centralizes the 2-decimal rounding rule (parseFloat(x.toFixed(2))) previously duplicated across kitchen, analyzer, and renderer, giving quantity/mass rounding a single documented implementation. No observable output changes — same rounding rule as before, just in one place. getASTnow throwsGramParseErrorinstead of a plainErroron syntax errors. This new error type includes structured fields (offsetandexpected) while preserving the original human-readable message. The language server now uses theoffsetfield to report parse-error diagnostics at their exact location in the document, rather than defaulting to line 1 column 1.- The playground now shows a red squiggly marker at the exact location of a syntax error using the new
offsetfield. Fixed a crash in the playground on mount in real browsers caused by an aggressive automated lint fix that removed necessary Vue<template>bindings and component imports. toHTMLno longer stamps footnote anchor ids withMath.random(). Output is now deterministic by default (ids likenote-1), which is required for byte-stable golden/conformance testing. If you render multiple recipes on the same page and relied on random ids to avoid anchor collisions, pass a newrenderIdoption (e.g. a recipe slug) to disambiguate.- Upgraded all monorepo dependencies to their latest versions and implemented a TypeScript 7 dual setup for faster typechecking while preserving compilation toolchain compatibility. Fixed type errors arising from Node 26 and VS Code LSP v10 updates.
Bug fixes and improvements
Section titled “Bug fixes and improvements”- Added a global
--verbose/--debugflag (works with any subcommand) that prints the full stack trace alongside the usual terse error message — useful when filing a bug report or diagnosing an unexpected failure. - Fix
_usageIdleaking a global counter across separatecompile()calls in the same process (affected the language server andgram scale’s parallel compiles, making ids non-deterministic for an unchanged recipe). Fix nutrition analysis always reportingisEstimate: trueregardless of actual data precision. Fix the section mass badge in HTML output missing its scale icon. - Fixed a regression introduced alongside the new
--verboseflag wheregram -vstopped printing the version and showed the help text instead (-vis citty’s own--versionshorthand — it’s no longer swallowed as part of the verbose flag). Also fixedgram import’s fetch timeout message not showing up when the timeout fires while reading a slow response body instead of during the initial connection. .envis now written with0600permissions instead of the OS default, so API keys are no longer group/world-readable on shared machines..gram/config.yamlis now validated at load time (invalid fields fail with a clear error instead of crashing deep in the pipeline).gram importnow times out after 15s, caps response bodies at 10MB, and asks for confirmation before writing AI-converted content from an untrusted external source to disk (skippable with--yes).- Performance improvements for CLI tools: -
gram formatnow processes files concurrently, significantly speeding up execution on large recipe collections. -gram db syncnow uses a length-based pre-check for fuzzy matching to speed up similarity comparisons against large databases. - Fixed a bug in
applyScale()where scaled quantities were incorrectly squared instead of multiplied for inline step ingredients, resulting in incorrect values (e.g., displaying800ginstead of400gwhen using--scale 2). The aggregated shopping list was unaffected. - chore: declare
sideEffects: falseso bundlers can tree-shake unused exports from these packages No package previously declared this, so third-party bundlers had to assume every module might have side effects and couldn’t safely drop unused code. - fix: sync TextMate grammar with the
^/~_sigil changes and stop mis-highlighting invalid temperature units - Updated TextMate grammar to use^(Temperature) and~_(Passive Timer) sigils. - Temperature unit highlighting now mirrors the compiler’s whitelist (e.g.,180C/180°F). Invalid units now receive a distinctinvalid.illegal.unit.gramscope. - Name matching now correctly stops at the new^sigil. - Compiler warnings (
CompilationResult.warnings,NutritionMetrics.warnings) are now always structuredWarningobjects ({ code, message, item?, loc?, section? }) instead of sometimes being plain strings depending on call order — a latent inconsistency that could previously produce"[object Object]"in some rendered output.Usage.composite,Usage.options, andProcessedStep.contentare now properly typed instead ofany. Also fixes range-based timer quantities (e.g.~{5-10min}) never displaying correctly ingram diffoutput, due to a pre-existing typo checking non-existent fields.
Breaking
Section titled “Breaking”validateIngredientDatabaseno longer throws an error on a single malformed entry. It now validates entry-by-entry, returning both valid data and rejected keys. This preventsgram checkorgram cookfrom hard-failing due to one unrelated bad line.physical.yieldmust now be> 0(previously>= 0) to prevent producingInfinitymass downstream.gram checknow only fails on structural issues (like undefined references) and uses a sharedwarningSeveritymap. Nutritional gaps and incomplete annotations are reported as warnings instead of failing the build. Use--strictfor the old all-warnings-fail behavior.GramConfigErrorexit code changed from 2 to 1 (user error, not internal crash).- The Temperature sigil is now
^(e.g.^{180C}).°is no longer a block-opening character, but remains valid inside unit spellings (°C). - The Timer passive marker is now
~_(e.g.~_{45min}) instead of~&. - Temperature units now accept bare
C/Fin addition to°C/°F.
- Added a guard in
applyYieldagainst non-positive yield factors. - Shopping list aggregation: The
optionalmodifier is now treated as an intersection rather than a union. diffRecipes: Temperature ranges that change bounds but keep the same average are now correctly detected in the diff. Fixed an issue where identical section titles would drop timer/temperature tokens.calculateMassMetrics: Excludesoptionalingredients fromtotalMassto match nutritional calculations.calculateNutrition: Missing nutrient data now propagates asundefinedrather than an indistinguishable0.- Language Server: Fixed a race condition where completions immediately after
@or&could return nothing. - Language Server: Diagnostics now correctly use the shared
warningSeveritymap. - Language Server:
ingredients.yamlis now actively watched via LSP. External edits instantly refresh diagnostics without restarting the editor.
New syntax
Section titled “New syntax”- Added support for mixed-number fractions (
1 1/2) and Unicode vulgar fraction glyphs (½).
Kitchen
Section titled “Kitchen”warningSeverity: a new exported map to separate structural errors from recoverable warnings.- Temperature units are now validated and normalized to canonical
°C/°F. slugifynow preserves non-Latin letters via\p{L}/\p{N}.
Analyzer
Section titled “Analyzer”- Fixed
parseDensityOverridesname normalization for accented ingredient names.
[1.0.0-beta.1] - 7/5/2026
Section titled “[1.0.0-beta.1] - 7/5/2026”Breaking changes
Section titled “Breaking changes”- refactor: rename
isAsynctoisPassiveto align with domain terminology
To better align the codebase with the actual domain model of a kitchen, the “asynchronous” timer concept has been completely renamed to “passive”. This introduces the following breaking changes for tool builders:
- AST: The
TimerASTnode propertyisAsync: booleanis nowisPassive: boolean. - CSS: The
.timer.asyncclass generated by the renderer has been renamed to.timer.passive. - Kitchen Metrics: All internal variables and comments referring to
asyncbackground tasks have been updated topassive. - Breaking Change: Refactored Physical Engine Nomenclature
The physical enrichment options and internal APIs have been renamed for clarity and to align with professional culinary terminology.
If you are using @gram-lang/analyzer programmatically, please update your configuration:
enableMassNormalizationis nowenableMassStandardizationenableYieldManagementis nowenableYieldCalculation- The exported
normalizeMasshelper is nowstandardizeMass
This update ensures total parity with the updated official documentation.
- Introduction of the official Gram CLI (
@gram-lang/cli), a comprehensive command-line tool to manage, compile, and interact with your recipes.
Project & Recipe Management:
- Project Setup:
gram initsafely scaffolds a.gramenvironment, including interactive AI provider setup (Google, OpenAI, Anthropic, Ollama) and secret management. - Automated Import:
gram importscrapes recipes from any URL and uses AI to flawlessly translate and convert them into native.gramsyntax. - Validation & Compilation:
gram checkinstantly validates syntax and database integrity.gram buildcompiles recipes to robust JSON with full physical and nutritional enrichment. - Smart Scaling & Diff:
gram scaledynamically resizes recipes (e.g.,--scale flour=300g) with a visual before/after comparison.gram diffprovides a semantic “git diff” for cooking, tracking changes in quantities, timings, and steps. - Export & Print:
gram printgenerates a beautifully typeset A4 HTML recipe card.gram exportconverts recipes to clean Markdown or static HTML. - Auto-formatting & Watcher:
gram formatautomatically enforces.gramlanguage styling standards.gram watchprovides real-time validation upon saving.
Interactive Cooking & Daily Use:
- Terminal Cooking Assistant (
gram cook): A step-by-step interactive TUI (Terminal User Interface) that guides you through the recipe, featuring a dynamic ingredient checklist and live background timers. - Smart Shopping List (
gram shop): Aggregates ingredients across multiple recipes, automatically converting volumes to masses and sorting them by supermarket aisle. - Recipe Search (
gram suggest): An alias-aware search engine to find recipes based on your available ingredients (e.g.,--with "butter, eggs" --without "milk"). - Rich Terminal Preview (
gram view): Renders recipes directly in the terminal with color coding, active timers, and nutritional tables.
AI-Powered Database Management (gram db):
- Smart Database Sync & Enrichment:
gram db syncautomatically tracks new ingredients across your project.gram db enrichuses AI to automatically fill in missing densities, unit weights, categories, and nutrition facts. - Semantic Linting & Conflict Resolution:
gram db lintuses AI to detect plural mistakes and semantic duplicates (e.g.,scallionvsgreen onion).gram db mergehandles conflicts when integrating external community databases.
New features
Section titled “New features”- docs: update timer terminology from synchronous/asynchronous to active/passive
To better align with real-world culinary concepts and eliminate confusion, the terminology for timers has been updated throughout the documentation.
Previously, Gram used computer-science terms (synchronous / asynchronous) to describe how timers affected the recipe flow. However, in a kitchen environment, almost all timers block the preparation itself, even if they run in the background.
To clarify this, we have shifted the terminology to focus on the cook’s availability rather than the execution thread:
- Synchronous timers are now referred to as Active timers. These timers require the cook’s attention and add to the
activeTimemetric. - Asynchronous (
~&) timers are now referred to as Passive (or Idle) timers. These timers represent background tasks (like resting or baking) that free up the cook to perform other steps concurrently.
Note: This is a purely conceptual nomenclature change to make the documentation and learning curve more intuitive for non-developers. The underlying syntax (~{} and ~&{}) and the compiler’s Gantt chart logic remain exactly the same.
-
Major overhaul of the documentation and playground infrastructure:
-
Documentation Rewrite & i18n: The documentation has been completely rewritten, thoroughly verified, and is now fully translated into French (in addition to the English version).
-
Advanced Vue 3 Playground: The legacy playground has been removed and rebuilt from the ground up using Vue 3. This new version is directly integrated into the documentation and introduces powerful new features, including recipe scaling and baker’s math.
-
The
@gram-lang/compilerpackage has been renamed to@gram-lang/kitchento fully embrace the Gram language’s domain identity. -
docs: integrate new Vitepress documentation site and embedded playground
-
Documentation: Complete overhaul of the Gram documentation site using Vitepress. Improved layout, better navigation, and comprehensive coverage of the new syntax and APIs.
-
Playground: Replaced the legacy standalone playground with a new, fully integrated version directly within the Vitepress documentation site. Features live-reloading, side-by-side editing, and syntax highlighting via Shiki.
-
Complete overhaul of the VS Code Extension with Language Server, Live Preview, and advanced assistance
Major New Features:
- Full Language Server (LSP): The extension now ships with a complete language server. All advanced features activate instantly upon opening a
.gramfile. - Dynamic Live Preview: A side-by-side WebView panel displaying the real-time HTML render of the recipe as you type.
- Gracefully handles syntax errors via a visual fallback.
- Integrates the official Playground styles for perfect consistency.
- CodeLens & Inlay Hints:
- Inlay Hints: Displays the cumulative total time in gray next to section titles.
- CodeLens: Clickable buttons
▶ Previewand📊 Macrosabove the main recipe title for quick access.
Ingredient Database & Nutrition:
- Automatic Loading: Automatically detects and loads
.gram/ingredients.yamlfrom the workspace root (or via the path configured ingram.ingredientDatabase.pathsettings). Optimized indexing (O(1)) for maximum performance. - Seamless Plural Management: The extension silently handles plurals (e.g.,
@carrots{3}will automatically matchcarrot). No error diagnostics are raised, everything is transparent. - Nutritional Hover & Macros Panel:
- Hovering over an ingredient displays a complete nutritional table (calories, proteins, carbs, fats, etc.).
- The Live Preview integrates a Macros panel (hidden by default) that can be shown via CodeLens, featuring clear visual alerts for ingredients missing data.
- Fuzzy Matching “Did you mean?”: Levenshtein algorithm (distance ≤ 2) suggests corrections if an ingredient is misspelled (e.g.,
@tomaatosuggests@tomato). Directly integrated into Quick Fix code actions. - On-Hover Unit Conversion: Automatically converts volumes (ml, tbsp, etc.) to grams when hovering over an ingredient if its density is known in the database.
Editing Assistance & Navigation:
- Advanced Contextual Autocompletion:
- After
@: Suggests ingredients and their aliases from the YAML database. Automatically adds curly braces{}for multi-word names. - After
&: Suggests only the existing intermediate declarations in the document. - Inside
{}: Suggests canonical units (mass, volume, time) and their aliases (French/English) as soon as a digit is typed.
- After
- Smart Navigation:
- Go to Definition (
Ctrl+Click/F12) on a&refreference to jump to its->&refdeclaration. - Find All References (
Shift+F12) to list all usages of an intermediate. - Rename Symbol (
F2) to atomically rename an intermediate (declaration + references). - Outline Panel showing the hierarchy of sections and elements, with foldable code in the gutter.
- Go to Definition (
- Intermediate Hover: Displays the full text of the step where the intermediate was created.
Diagnostics & Code Actions (Quick Fixes):
- Real-Time Diagnostics:
- Red underline for an orphaned
&refreference. - Warning for an unused
->&refdeclaration. - Warning if the frontmatter (title) is missing.
- Red underline for an orphaned
- Code Actions (💡):
- Quick Fix: Insert the missing
title:in the frontmatter. - Quick Fix: Remove an unused intermediate declaration.
- Quick Fix: Declare a missing intermediate for an existing reference.
- Refactor: Convert a volume quantity to mass directly in the code.
- Refactor: Replace a misspelled ingredient with the closest Fuzzy match suggestion.
- Quick Fix: Insert the missing
Semantic Highlighting, Formatting & Snippets:
- Semantic Tokens: Regex-based syntax highlighting (TextMate) is replaced by AST-driven highlighting. Offers surgical precision for ingredients, timers, temperatures, comments, and nested units (e.g., precise separation of value and unit in
{10g}). - Auto-formatting (
Alt+Shift+F):- Normalization of spaces inside curly braces (
{ 200 g }→{200g}). - Normalization of composite ingredients (
@a < @b→@a<@b). - Alignment of section titles and tab-to-space conversions.
- Normalization of spaces inside curly braces (
- Built-in Snippets: Keyboard shortcuts for the basic structure (frontmatter, sections, steps), ingredient variants (
@ing,@?,@-), cookware (#cw), and timers/temperatures. - Core language update to enforce new syntax adjustments (breaking changes).
- Grammar & Parsing:
- Made
{}optional for single-word ingredients. - Replaced temperature symbol with
°. - Replaced async timers with
~&{}. - Replaced aliases brackets with
:. - Tightened
<@with no spaces allowed. - Added fixed modifier
=. - Mandatory
@&inside relative quantities declarations. (e.g:@water{60% @&flour}) - Top-Level Support: Allowed writing comments and recipe steps globally, anywhere in the document (even before the first
## Section).
- Made
- Compiler Analysis:
- Added
INVALID_MODIFIER_COMBINATIONwarnings. - Updated AST transformations to support the new modifiers parsing rules and the implicit top-level sections grouping.
- Added
- Aligned visual tooling and test recipes with the new grammar.
GramHighlight.ts(Playground & VSCode): Updated all syntax highlighting regular expressions to accurately match the new syntax (new temperature symbols, updated modifiers, etc.).- Examples (
basquaise_chicken.gram,lemon_meringue_pie.gram,torture.gram): Fully migrated recipe code to comply with the new grammar.
-
Fixed several correctness bugs found during a documentation audit, and added alias-aware cross-unit shopping list aggregation.
-
@gram-lang/analyzer:
unit_weight-based conversions (e.g.@avocado{1}) were being double-divided byyield— the whole-unit weight is now correctly treated as Gross Mass, with Net Mass derived forward (Gross × yield), while explicit mass/volume entries keep deriving Gross backward (Net ÷ yield) as before. Optional ingredients (?) are no longer counted in nutrition totals. NewresolveCanonicalId()resolves an ingredient name/alias to its database key, and a newaggregateShoppingList()step re-groups the shopping list by canonical id — merging aliased ingredients (e.g.beurre/butter) and cross-unit quantities (e.g.100g+1 cup) into a single gram total whenever every entry resolves to a mass, falling back to separate entries flaggedmultiUnit: truewhen a density is missing. -
@gram-lang/renderer: The HTML shopping list now clusters consecutive
multiUnit-flagged entries for the same ingredient under one heading with a “⚠️ Mixed units” badge, instead of listing them as unrelated lines. -
@gram-lang/cli:
shopper’s alias resolution now reuses@gram-lang/analyzer’sresolveCanonicalId()instead of a separate, duplicated alias map. -
- Playground: Migrated syntax highlighting engine from Highlight.js to Shiki. The playground now natively uses the official VSCode TextMate grammar, ensuring 100% consistency across environments.
-
VSCode Extension: Improved syntax coloring by mapping custom Gram tokens (cookware, intermediate ingredients, units) to standard semantic TextMate scopes, restoring vibrant and legible colors across all VSCode themes.
-
Improve ingredient preparation tracking and display.
-
Kitchen:
aggregateSectionIngredientsnow groups ingredients by bothidandpreparation, creating separate entries for the same ingredient if it requires different preparations (e.g. cold vs melted). -
Renderer: A new
formatModeoption in the render context controls preparation rendering. Preparations remain visible in inline text to prevent information loss. In the section’s ingredient list (Mise-en-place), they are cleanly displayed with an em-dash. In the global shopping list, they remain hidden. -
Refactor TextMate grammar to
@gram-lang/parser
The TextMate grammar (gram.tmLanguage.json) has been moved from @gram-lang/vscode-extension to @gram-lang/parser to colocate the structural (Ohm) and lexical (TextMate) definitions of the Gram language.
This resolves architectural issues where consumers like the playground had to perform brittle, deep relative imports into the VSCode extension package. The syntax grammar is now officially exported and accessible via @gram-lang/parser/textmate.
-
Added a centralized
ScaleEnginein@gram-lang/kitchento make recipe scaling (--scale) and Baker’s Percentage math safer and more consistent everywhere. -
@gram-lang/kitchen: New
resolveScaleFactor()/applyScale()API validates a--scaletarget before computing a factor — rejecting fixed (@=) ingredients, relative quantities, ingredients only used inside a sub-recipe, ingredients inside an alternative-ingredient group, and ingredients split across incompatible units, with a clear error instead of a silently wrong number. A sub-recipe’s own total (e.g. “2 lemons”) is itself a valid scale target. Scaling is now a pure operation (never mutates the original recipe), and the compiled recipe now carries an explicitscaleFactorfield. Covered by a new unit test suite. -
@gram-lang/analyzer: Fixed the
@*Baker’s-reference auto-detection (it silently never matched before), and it now refuses to use a relative-quantity ingredient as the 100% base instead of computing bogus percentages. The enriched JSON AST now natively includes abakersPercentagefield for all ingredients if a reference ingredient is declared or passed via thebakersReferenceoption.convertUnit()now accepts an optional density (g/mL) to bridge mass ↔ volume conversions; newresolveIngredientDensity()andparseDensityOverrides()helpers resolve that density from a recipe’sdensities:frontmatter. Also includes a critical null-safety fix when parsing recipes containing standalone comments. -
@gram-lang/renderer: Natively supports formatting Baker’s Percentages provided by the analyzer (for HTML and Markdown exports, and the Playground), removing its legacy calculation logic. Fixed
gram print --bakers-math-onlyhaving no effect. -
@gram-lang/cli: The CLI now cleanly acts as a presentation layer for Baker’s Math, reading percentages directly from the AST. Added
--bakers-math,--bakers-reference, and--bakers-math-onlyflags to theview,printandexportcommands.--scale id=valuenow supports same-family unit conversion and cross-family conversion when a density is available; suggests the closest matching ingredient name on a typo; no longer shows corrupted comparison rows for an ingredient split across multiple units ingram scale. -
Major architecture refactoring, ESM Migration, performance optimizations and code cleanup
-
Complete overhaul of the documentation to reflect the new syntax changes.
Bug fixes and improvements
Section titled “Bug fixes and improvements”-
Fix: Allow preparations on bare ingredients and cookware Previously, the parser and the TextMate syntax highlighter required quantity braces
{}to attach a preparation to an element (e.g.@butter{}(melted)or#pan{}(20cm)). The grammar has been updated to support attaching preparations directly to bare elements without braces. You can now write@butter(melted)or#pan(20cm)naturally. The AST will correctly extract thepreparationproperty, and your editor will highlight it properly. -
NutritionMetrics output field renamed from
salttosodiumto match the ingredient database schema -
Refined formatting and mass normalization for relative quantities:
-
Cleaner Display: Relative quantities are now seamlessly displayed without internal
@or&markers (e.g.,125% of lemon juice). The redundant formula brackets[125% of...]have been removed from inline instructions. -
Robust Shopping List Aggregation: The compiler (
@gram-lang/kitchen) now strictly tracks ingredient lineage using_usageIds. This allows the analyzer (@gram-lang/analyzer) to flawlessly compute exact masses for complex items in the shopping list without confusing standard ingredients and their alternatives. -
Shopping List Accuracy: When mass normalization is enabled, the shopping list will now accurately display the fully resolved physical mass for relative quantities (e.g.,
sugar (156 g)) instead of falling back to the formula string. -
Fixed an issue in the parser where composite ingredients without braces (e.g.,
<@lemon,) would incorrectly consume subsequent text on the same line, causing missing ingredients and breaking relative quantity resolutions. -
Fix total recipe time calculation when using background timers
Previously, the compiler did not wait for passive tasks (like resting dough in the fridge) to finish before letting you use the result. This caused the estimated “Total Time” to be unrealistically short. The engine now properly understands dependencies and waits for intermediate preparations to be fully ready before proceeding to steps that need them.
[1.0.0-beta.0] - 6/29/2026
Section titled “[1.0.0-beta.0] - 6/29/2026”Breaking changes
Section titled “Breaking changes”- Introduction of the official GRAM CLI (
@gram-lang/cli), a comprehensive command-line tool to manage, compile, and interact with your recipes.
Project & Recipe Management:
- Project Setup:
gram initsafely scaffolds a.gramenvironment, including interactive AI provider setup (Google, OpenAI, Anthropic, Ollama) and secret management. - Automated Import:
gram importscrapes recipes from any URL and uses AI to flawlessly translate and convert them into native.gramsyntax. - Validation & Compilation:
gram checkinstantly validates syntax and database integrity.gram buildcompiles recipes to robust JSON with full physical and nutritional enrichment. - Smart Scaling & Diff:
gram scaledynamically resizes recipes (e.g.,--scale flour=300g) with a visual before/after comparison.gram diffprovides a semantic “git diff” for cooking, tracking changes in quantities, timings, and steps. - Export & Print:
gram printgenerates a beautifully typeset A4 HTML recipe card.gram exportconverts recipes to clean Markdown or static HTML. - Auto-formatting & Watcher:
gram formatautomatically enforces.gramlanguage styling standards.gram watchprovides real-time validation upon saving.
Interactive Cooking & Daily Use:
- Terminal Cooking Assistant (
gram cook): A step-by-step interactive TUI (Terminal User Interface) that guides you through the recipe, featuring a dynamic ingredient checklist and live background timers. - Smart Shopping List (
gram shop): Aggregates ingredients across multiple recipes, automatically converting volumes to masses and sorting them by supermarket aisle. - Recipe Search (
gram suggest): An alias-aware search engine to find recipes based on your available ingredients (e.g.,--with "butter, eggs" --without "milk"). - Rich Terminal Preview (
gram view): Renders recipes directly in the terminal with color coding, active timers, and nutritional tables.
AI-Powered Database Management (gram db):
- Smart Database Sync & Enrichment:
gram db syncautomatically tracks new ingredients across your project.gram db enrichuses AI to automatically fill in missing densities, unit weights, categories, and nutrition facts. - Semantic Linting & Conflict Resolution:
gram db lintuses AI to detect plural mistakes and semantic duplicates (e.g.,scallionvsgreen onion).gram db mergehandles conflicts when integrating external community databases.
New features
Section titled “New features”- The
@gram-lang/compilerpackage has been renamed to@gram-lang/kitchento fully embrace the Gram language’s domain identity. - Complete overhaul of the VS Code Extension with Language Server, Live Preview, and advanced assistance
Major New Features:
- Full Language Server (LSP): The extension now ships with a complete language server. All advanced features activate instantly upon opening a
.gramfile. - Dynamic Live Preview: A side-by-side WebView panel displaying the real-time HTML render of the recipe as you type.
- Gracefully handles syntax errors via a visual fallback.
- Integrates the official Playground styles for perfect consistency.
- CodeLens & Inlay Hints:
- Inlay Hints: Displays the cumulative total time in gray next to section titles.
- CodeLens: Clickable buttons
▶ Previewand📊 Macrosabove the main recipe title for quick access.
Ingredient Database & Nutrition:
- Automatic Loading: Automatically detects and loads
.gram/ingredients.yamlfrom the workspace root (or via the path configured ingram.ingredientDatabase.pathsettings). Optimized indexing (O(1)) for maximum performance. - Seamless Plural Management: The extension silently handles plurals (e.g.,
@carrots{3}will automatically matchcarrot). No error diagnostics are raised, everything is transparent. - Nutritional Hover & Macros Panel:
- Hovering over an ingredient displays a complete nutritional table (calories, proteins, carbs, fats, etc.).
- The Live Preview integrates a Macros panel (hidden by default) that can be shown via CodeLens, featuring clear visual alerts for ingredients missing data.
- Fuzzy Matching “Did you mean?”: Levenshtein algorithm (distance ≤ 2) suggests corrections if an ingredient is misspelled (e.g.,
@tomaatosuggests@tomato). Directly integrated into Quick Fix code actions. - On-Hover Unit Conversion: Automatically converts volumes (ml, tbsp, etc.) to grams when hovering over an ingredient if its density is known in the database.
Editing Assistance & Navigation:
- Advanced Contextual Autocompletion:
- After
@: Suggests ingredients and their aliases from the YAML database. Automatically adds curly braces{}for multi-word names. - After
&: Suggests only the existing intermediate declarations in the document. - Inside
{}: Suggests canonical units (mass, volume, time) and their aliases (French/English) as soon as a digit is typed.
- After
- Smart Navigation:
- Go to Definition (
Ctrl+Click/F12) on a&refreference to jump to its->&refdeclaration. - Find All References (
Shift+F12) to list all usages of an intermediate. - Rename Symbol (
F2) to atomically rename an intermediate (declaration + references). - Outline Panel showing the hierarchy of sections and elements, with foldable code in the gutter.
- Go to Definition (
- Intermediate Hover: Displays the full text of the step where the intermediate was created.
Diagnostics & Code Actions (Quick Fixes):
- Real-Time Diagnostics:
- Red underline for an orphaned
&refreference. - Warning for an unused
->&refdeclaration. - Warning if the frontmatter (title) is missing.
- Red underline for an orphaned
- Code Actions (💡):
- Quick Fix: Insert the missing
title:in the frontmatter. - Quick Fix: Remove an unused intermediate declaration.
- Quick Fix: Declare a missing intermediate for an existing reference.
- Refactor: Convert a volume quantity to mass directly in the code.
- Refactor: Replace a misspelled ingredient with the closest Fuzzy match suggestion.
- Quick Fix: Insert the missing
Semantic Highlighting, Formatting & Snippets:
- Semantic Tokens: Regex-based syntax highlighting (TextMate) is replaced by AST-driven highlighting. Offers surgical precision for ingredients, timers, temperatures, comments, and nested units (e.g., precise separation of value and unit in
{10g}). - Auto-formatting (
Alt+Shift+F):- Normalization of spaces inside curly braces (
{ 200 g }→{200g}). - Normalization of composite ingredients (
@a < @b→@a<@b). - Alignment of section titles and tab-to-space conversions.
- Normalization of spaces inside curly braces (
- Built-in Snippets: Keyboard shortcuts for the basic structure (frontmatter, sections, steps), ingredient variants (
@ing,@?,@-), cookware (#cw), and timers/temperatures. - Core language update to enforce new syntax adjustments (breaking changes).
- Grammar & Parsing:
- Made
{}optional for single-word ingredients. - Replaced temperature symbol with
°. - Replaced async timers with
~&{}. - Replaced aliases brackets with
:. - Tightened
<@with no spaces allowed. - Added fixed modifier
=. - Mandatory
@&inside relative quantities declarations. (e.g:@water{60% @&flour}) - Top-Level Support: Allowed writing comments and recipe steps globally, anywhere in the document (even before the first
## Section).
- Made
- Compiler Analysis:
- Added
INVALID_MODIFIER_COMBINATIONwarnings. - Updated AST transformations to support the new modifiers parsing rules and the implicit top-level sections grouping.
- Added
- Aligned visual tooling and test recipes with the new grammar.
GramHighlight.ts(Playground & VSCode): Updated all syntax highlighting regular expressions to accurately match the new syntax (new temperature symbols, updated modifiers, etc.).
- Major architecture refactoring, ESM Migration, performance optimizations and code cleanup
- Complete overhaul of the documentation to reflect the new syntax changes.
Bug fixes and improvements
Section titled “Bug fixes and improvements”- NutritionMetrics output field renamed from
salttosodiumto match the ingredient database schema
[0.10.1] - 6/14/2026
Section titled “[0.10.1] - 6/14/2026”Bug fixes and improvements
Section titled “Bug fixes and improvements”- Cleaned up and updated dependencies.
- Implemented Turborepo to optimize and simplify project building.
[0.10.0] - 6/14/2026
Section titled “[0.10.0] - 6/14/2026”New features
Section titled “New features”- Added Bun snapshot testing to the development environment for compiler validation.
- Created a new shared @gram-lang/renderer package to handle HTML and Markdown generation.
- Refactored unit translation and normalization into a new centralized @gram-lang/i18n package to remove redundancy between the compiler and analyzer.
Bug fixes and improvements
Section titled “Bug fixes and improvements”- Refactored the analyzer to reuse the compiler’s getNumericQty utility, improving code DRYness and type safety.
- Migrated to full Bun environment using ‘workspace:*’ dependencies
- Added Zod to automatically catch invalid data and prevent crashes.
- Improved code safety in the compiler by adding strict type checking for recipe elements.
- Introduce a unified
getNumericQtyutility inutils.tsto safely extract numeric values from AST Quantity structures (including fractions, ranges, and nested nodes). This fixes a bug where composite child ingredient quantities using fractions (e.g.@zest{1/2}) aggregated to zero in the shopping list. - Format decimal values strictly below 1 (e.g.
0.5,0.25) as clean culinary fractions (1/2,1/4) in the shopping list, while keeping standard decimal formatting for values greater than or equal to 1 (e.g.1.5). - Cleaned up repetitive code that manages and saves ingredients and cookware.
- Standardized the warning system to provide consistent and reliable error messages across all tools.
- Improved parser stability and removed complex build workarounds for web environments.
- Refactored AST processing for improved maintainability.
- Standardized how recipe elements are identified across the system to prevent typos and errors.
[0.9.0] - 6/7/2026
Section titled “[0.9.0] - 6/7/2026”New features
Section titled “New features”- Ingredients states (
@ingredient:state{}) are removed. While the idea was to better organize the ingredient’s database, it just happened to be too confusing to use. - Updated unit resolution to allow for french aliases (e.g: tsp = càc)
- Ingredient references without quantities (
@&ingredient{}) are now excluded from section ingredients summaries. This keeps section-level mise en place lists clean by filtering out pure flow instructions (like removing or re-inserting) while preserving separate measured portions. - Fixed multiple issues with mass calculations
- Extracted analyzer logic (mass normalization, yield management, nutritional estimation) into its own package. Ingredients database must now be provided by the user.
- GRAM syntax now also accepts semantic temperatures (e.g: Cook on !{low heat})
Bug fixes and improvements
Section titled “Bug fixes and improvements”- Cleaned repo with removal of /dist folders
- Using changesets to simplify changelog management
- Clarification of global comments for a recipe, to be declared in the front matter as “notes”
- Removed unwanted spaces from default playground input
- Empty ingredient masses now count as zero.