This repository has been archived on 2026-07-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
kitchen-md/.claude/CONTEXT.md
alexion 5841ae5de8 chore: add Biome and document design decisions from grill session
- 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)
2026-07-08 23:29:09 -04:00

137 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `NN` 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 `.md` extension, 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.