- Add @biomejs/biome 2.5.3 with 2-space indent, double quotes, trailing commas - Add lint and format scripts to root package.json - Update CONTEXT.md: resolve Structured Output shape, add Document AST, Block, Inline Node terms - Add ADR 0002 (remark as internal Markdown parser) - Add ADR 0003 (core defines own AST types) - Add spec: core-parser (Parser implementation and Document AST) - Add spec: cli-view (kitchen view <file> command)
6.8 KiB
KitchenMD — Domain Glossary
A living glossary of canonical terms for this project. Implementation details belong in specs or code, not here.
Annotation
An inline sigil-based marker embedded in recipe prose that carries structured data. There are exactly three annotation types: Ingredient, Cookware, and Timer. An annotation is valid Markdown plain text — it does not break rendering in Obsidian or any standard Markdown renderer. Annotations follow CommonMark's inline processing rules: parsed wherever CommonMark processes inline content (paragraphs, headings, list items, blockquotes); ignored wherever CommonMark treats content as literal (code spans, code blocks, HTML comments, raw HTML blocks).
Ingredient Annotation
Syntax: @name{quantity} or @name{quantity unit}
The { character terminates the name, so multi-word names work without escaping.
The {} wrapper is mandatory — bare @name without braces is not valid.
Quantity is optional; @black pepper{} is valid with no quantity.
Valid quantity formats: integer (2), decimal (0.5), simple fraction (1/2), mixed number (1 1/2).
Unit is optional and free-form — the format places no constraints on what unit string is written.
When both quantity and unit are present, they are delimited by the last space inside the braces — so @butter{1 1/2 tbsp} parses as quantity 1 1/2, unit tbsp.
Valid name characters: any character except {, }, @, $, ~.
Cookware Annotation
Syntax: $name{}, $name{quantity}, or $name{quantity unit}
Same {-terminated name rule as Ingredient.
The {} wrapper is mandatory.
Quantity is optional; $pan{} is valid with no quantity.
Quantity and unit follow the same rules as Ingredient: valid formats are integer, decimal, simple fraction, mixed number; unit is free-form; delimited by the last space inside the braces.
Valid name characters: any character except {, }, @, $, ~.
Timer Annotation
Syntax: ~N unit or ~N-N unit
No braces.
The ~ sigil is followed immediately by a number or N–N range, a space, then a time unit word from the known set.
N follows the same format rules as ingredient/cookware quantity: integer, decimal, simple fraction, or mixed number.
Supports natural range syntax: ~10-15 mins.
In range form, the - separator follows the first quantity expression — since negative quantities don't exist, the - is unambiguous. ~1/2-1 hr parses as a range of 1/2 hr to 1 hr.
The annotation terminates after the unit word — trailing prose is ignored. ~1 min more parses as a timer of 1 min; "more" is plain text.
Known unit set (aliases → canonical abbreviation): sec, secs, second, seconds → s; min, mins, minute, minutes → min; hr, hrs, hour, hours → hr.
Recipe File
A valid .md file that may contain any combination of Obsidian Flavored Markdown, YAML frontmatter, and Annotations.
The file renders correctly in Obsidian without a plugin.
A Recipe File contains prose instructions only — there is no separate ingredients list section.
Ingredients are derived entirely by the Parser from inline Ingredient Annotations.
Step
The atomic unit of a recipe instruction. A step is a single non-empty line in a prose context, or a single ordered list item. Headings are not steps. Step numbers are section-local — they reset at each heading and do not change when steps are added to other sections.
Combined Recipe
A Recipe File whose purpose is to sequence steps from two or more other Recipe Files. It contains no steps or ingredients of its own — only Step References in the order they should be executed.
Step Reference
A KitchenMD extension of Obsidian transclusion syntax for embedding a specific step from another Recipe File.
Syntax:
-
![[filename#section-heading:N]]— step N within a named section. -
![[filename#N]]— step N in a headingless recipe (bare number, no section prefix). -
filename— the Recipe File to reference (no.mdextension, matching Obsidian wikilink convention). -
section-heading— the heading text of the section containing the step. -
N— the 1-based step number within that section or file.
Examples:
![[italian meatballs#rolling:2]]— second step of the "rolling" section.![[basic brine#2]]— second step of a headingless recipe.
This syntax is a compatibility break with vanilla Obsidian: #section:N does not match any Obsidian heading anchor, so Step References will not render correctly without KitchenMD tooling.
Frontmatter
YAML metadata block at the top of a Recipe File (standard --- delimiters).
The parser passes frontmatter through as-is with no schema enforcement.
Consumers are responsible for reading whatever fields they need.
Sigil
The single punctuation character that opens each annotation type:
@— Ingredient$— Cookware~— Timer
Parser
The TypeScript library (@kitchen-md/core) that accepts a Recipe File string and returns a structured data object.
It is a pure function of its input string — no filesystem access.
Structured Output
The Document AST returned by the Parser for a given Recipe File. See Document AST for the exact shape.
Document AST
The full structured representation of a Recipe File returned by the Parser.
Top-level shape: { frontmatter: Record<string, unknown>, blocks: Block[] }.
frontmatter is the raw YAML metadata, passed through without schema enforcement.
blocks is a flat, ordered list of Block nodes representing the document body in document order.
The flat structure means headings and their following content are siblings, not parent/child.
Consumers that need section grouping derive it by scanning for Heading nodes.
Core defines its own AST node types; remark (the internal Markdown parser) is a private implementation detail and its types do not appear in the public API.
See ADR 0002 and ADR 0003.
Block
A top-level node in the Document AST's blocks array.
Each Block represents one logical unit of document structure: a heading, a paragraph, a list, a code block, etc.
Block types are defined by core; the exact catalogue is not yet finalised — see open questions below.
Inline Node
A node representing inline content within a Block (e.g. within a paragraph or list item). Inline nodes include: plain text, emphasis, strong, code span, link, Wikilink, and the three Annotation types (Ingredient, Cookware, Timer).
Open / Unresolved
- Block node type catalogue — the exact set of Block types core defines (heading, paragraph, list, code block, blockquote, etc.) and how unrecognised types are represented.
- Unit normalisation — the parser should normalise known aliases to a canonical abbreviation (
grams→g,kilograms→kg); exact alias table and canonical forms are TBD. This is a parser concern, not a format constraint.