avodado
avodado docs

Architecture

A pnpm monorepo where dependencies point inward to a pure core — the block registry, the reference resolver, and files-are-truth.

Avodado is a pnpm monorepo of seven published packages — @avodado/core, render, export, sync, studio, mcp, cli — where dependencies always point inward toward a pure @avodado/core. This page is the condensed version; the canonical document is ARCHITECTURE.md in the repo.

Guiding principle

The files on disk are the only source of truth. The CLI, agents, and any UI are editors and consumers of those files; none owns state. The core library turns files into a validated in-memory model with a reference graph; everything else consumes that model.

Layering

@avodado/core   ← pure: parse, schemas (77 + aliases), validate, resolve, edit ops. No I/O.
@avodado/render ← @avodado/core. HTML string out. No DOM, no browser. Theme support.
@avodado/export ← @avodado/{core, render}. HTML + PDF (Playwright).
@avodado/sync   ← @avodado/core. Imports external sources (OpenAPI) into documents.
@avodado/studio ← @avodado/{core, render}. Browser SPA (Vite/React), ships built static assets.
@avodado/mcp    ← @avodado/{core, render, sync}. MCP server over stdio.
@avodado/cli    ← @avodado/{core, render, export, studio, sync}. Ink TUI. Owns process.exit.

The rules:

  • @avodado/core does no I/O: no file system, no network, no process, no DOM. It reads strings and returns models and diagnostics.
  • All I/O lives in the outer rings (cli, export).
  • Libraries return diagnostics as values. They don't throw for expected conditions (parse errors, schema violations, dangling refs). The CLI is the only layer that maps diagnostics to console output and exit codes.

The block registry

The block registry in @avodado/core is the architectural backbone:

export const blockSchemas = {
  meta, callout, table, sequence, erd, userstory, timeline, kanban, tracker,
  // … one entry per block type — 77 in total, across 12 families.
} as const satisfies Record<BlockType, ZodTypeAny>;

export type BlockDataMap = { [K in BlockType]: z.infer<(typeof blockSchemas)[K]> };

The same Record<BlockType, …> pattern propagates to every rendering target — @avodado/render defines HtmlRendererRegistry = { [K in BlockType]: (data: BlockDataMap[K]) => string }. Adding a new block type in core is a one-line change to BLOCK_TYPES, and tsc then immediately surfaces every registry that hasn't been extended.

This is intentional: there's exactly one place to add a block (its schema), and the type system makes every consumer update in lock-step. No scattered switch statements, no runtime default: throw clauses to keep in sync.

Rendering

@avodado/render emits self-contained HTML: the house CSS is namespaced under .docskin, SVG diagrams use integer-only coordinates so snapshots are byte-deterministic, and the six themes are pure CSS-variable overrides on the .docskin root — so diagrams retint with the prose, no SVG regeneration. See Slides & theming.

The reference scheme

Any block may carry a repo-global unique id; a reference is doc#id or #id. The resolver builds the id map and reference edges across all docs and emits diagnostics for duplicate ids and dangling refs — details in References.

The studio, in one paragraph

@avodado/studio is an outermost consumer of core + render (never export, which is Node/Playwright-bound). The whole parse → validate → render pipeline runs client-side in the browser; the avo studio server is a file bridge — JSON read/write API + SSE change events, bound to 127.0.0.1 — and never renders. Editing goes through core's surgical edit ops (replaceBlockBody, insertBlock, setYamlPath, …), so a studio session rewrites individual fenced blocks in place and the files on disk stay the single source of truth. More in the Studio guide.

Packages

PackagePurpose
@avodado/coreParser, Zod block schemas (all 77 types + 12 permanent aliases), validation, reference resolver. Pure (no I/O).
@avodado/renderrenderDocument (standalone HTML) + embeddable parts. Inline CSS + SVG, 6 themes.
@avodado/exporttoHtml(doc) + toPdf(doc) (Playwright headless Chromium).
@avodado/cliavo — the full command set.
@avodado/syncGenerate Avodado docs from external sources (OpenAPI).
@avodado/mcpMCP server exposing the doc tooling to any MCP client.

Tooling: pnpm workspace · tsup (ESM) · Vitest (650+ tests) · ESLint + typescript-eslint · Changesets. Strict TypeScript throughout (strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, …) — no any in public APIs.