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)
This commit is contained in:
2026-07-08 23:29:09 -04:00
parent d37988ec59
commit 5841ae5de8
8 changed files with 390 additions and 5 deletions

View File

@@ -0,0 +1,39 @@
# ADR 0002 — remark as Core's Internal Markdown Parser
**Status**: Accepted
## Context
`@kitchen-md/core` must parse CommonMark Markdown (plus Obsidian Flavored Markdown extensions) to build a Document AST.
The spec requires that Annotations follow CommonMark's inline processing rules — they are parsed inside paragraphs and list items, and ignored inside code spans and code blocks.
Correctly detecting those boundaries from scratch requires a complete CommonMark implementation.
Two approaches were considered:
**Option A — Use remark/unified**
remark is a CommonMark-compliant Markdown parser that produces mdast (a typed Markdown AST).
It has a plugin/transform architecture: OFM extensions (wikilinks, transclusion) are handled by existing community plugins (`remark-wiki-link`), and KitchenMD annotations are handled by a custom transform plugin that walks mdast text nodes and splits them into annotation nodes.
TypeScript support is first-class.
**Option B — Build the Markdown parser from scratch**
Write a custom CommonMark parser as part of `@kitchen-md/core`, with no external Markdown dependency.
## Decision
**Option A** — remark/unified as core's internal Markdown parser.
## Rationale
CommonMark is a large, precisely specified standard.
Building a correct implementation from scratch is significant work with no unique value to the project — the interesting problems are annotation parsing and document rendering, not Markdown tokenisation.
The "built from scratch" statement in the project spec refers to not forking Cooklang's parser, not to avoiding all external dependencies.
remark's plugin architecture maps cleanly to core's needs: one plugin for OFM wikilinks, one plugin for KitchenMD annotations, both operating as transform passes over an existing mdast.
## Consequences
- remark and its plugin ecosystem are build-time dependencies of `@kitchen-md/core`.
- remark must remain a private implementation detail — its types must not appear in core's public API (see ADR 0003).
- OFM support requires `remark-wiki-link` (and the underlying `micromark-extension-wiki-link` / `mdast-util-wiki-link`).
- KitchenMD annotation parsing is implemented as a custom remark transform plugin within core.

View File

@@ -0,0 +1,39 @@
# ADR 0003 — Core Defines Its Own AST Types
**Status**: Accepted
## Context
remark produces mdast — a well-typed Markdown AST with its own node type definitions.
`@kitchen-md/core` builds its Document AST on top of remark's output.
Two options exist for what core exposes in its public API:
**Option A — Expose mdast types directly**
Core re-exports remark's node types, augmented with KitchenMD annotation nodes via TypeScript module augmentation.
Consumers import `@kitchen-md/core` and receive mdast-typed nodes.
**Option B — Core defines its own AST types**
Core defines its own `DocumentAST`, `Block`, and `InlineNode` types.
remark is an internal implementation detail; its types never appear in the public API.
Consumers import only from `@kitchen-md/core`.
## Decision
**Option B** — core defines its own AST types.
## Rationale
The public API is a long-lived contract with multiple future consumers: the `kitchen` CLI, and the future Obsidian plugin.
Leaking mdast into that contract means any remark major version that changes mdast types breaks all consumers, even if core's own logic is unchanged.
mdast also exposes many node types that are irrelevant to KitchenMD consumers.
Core's own types can be a clean, minimal vocabulary scoped to what consumers actually need.
The future Obsidian plugin should not need to know or care that remark is involved.
## Consequences
- Core maintains its own type definitions for all public AST nodes (Document AST, Block types, Inline Node types).
- An internal translation layer maps remark's mdast nodes to core's types before returning from the Parser.
- Changing core's internal Markdown parser in the future does not require a public API change.
- Consumers do not take a transitive dependency on remark.