@hex-core/payload
Pure-function builders for the canonical LLM context markdown.
Install
0.2.1Runtime dependencypnpm add @hex-core/payloadWhat it does
`@hex-core/payload` is the pure-function builder for the canonical LLM context blob. Pass it a theme + components (+ optional recipes, density), get markdown back.
It exists so non-MCP-client surfaces — Next.js Server Components, generator scripts, CI fixtures, CLI tools — can render the canonical payload without spawning a subprocess and speaking JSON-RPC over stdio. The MCP server (`@hex-core/mcp@0.4.0+`) is now a thin transport shell that wraps this package.
The package also bundles the registry data (~59 items at last release) inside its tarball, so loaders work without filesystem coupling to the consumer's repo. This is what unblocked the studio's `/copy` route from re-implementing the markdown format locally.
Public API
buildAppContext
function buildAppContext(input: {
theme: { requested: string; resolved: Theme };
components: { slug: string; item: RegistryItem | null }[];
recipes?: { slug: string; recipe: Recipe }[];
density?: "compact" | "comfortable" | "spacious";
overrides?: Record<string, string>;
}): string;Build the full canonical markdown blob: install commands, `globals.css`, `tailwind.config.ts`, components, recipes, and the LLM context prompt. The `theme.requested` field surfaces in the markdown so the LLM knows whether the user picked the theme or fell back to default.
import { buildAppContext, loadRegistryItem } from "@hex-core/payload";
import { defaultTheme } from "@hex-core/tokens";
const components = ["button", "card", "input"].map((slug) => ({
slug,
item: loadRegistryItem(slug),
}));
const markdown = buildAppContext({
theme: { requested: "default", resolved: defaultTheme },
components,
density: "comfortable",
});
// Render markdown in a Server Component, write to disk, etc.buildFigmaTokens
function buildFigmaTokens(theme: Theme): {
$schema: string;
[collection: string]: unknown;
};Build a Figma Variables JSON document from a theme. Drop the result into the Figma plugin's import slot and the design system's tokens land in the file as variables. Round-trips with the same `Theme` shape that powers `themeToCss`.
import { buildFigmaTokens } from "@hex-core/payload";
import { defaultTheme } from "@hex-core/tokens";
import { writeFileSync } from "node:fs";
writeFileSync(
"./design-tokens.figma.json",
JSON.stringify(buildFigmaTokens(defaultTheme), null, 2),
);loadRegistryItem / loadRegistry
function loadRegistryItem(slug: string): RegistryItem | null;
function loadRegistry(): readonly RegistryItem[];Read from the bundled registry snapshot. `loadRegistryItem` returns `null` for an unknown slug; `loadRegistry` enumerates the whole catalog. Server-only — both functions perform synchronous filesystem reads, so don't import them from a Client Component.
import { loadRegistry, loadRegistryItem } from "@hex-core/payload";
const all = loadRegistry();
console.log(all.length); // 59 (at @0.2.0)
const button = loadRegistryItem("button");
if (button) {
console.log(button.ai.whenToUse);
}loadRecipes / loadRecipe
function loadRecipes(): readonly Recipe[];
function loadRecipe(slug: string): Recipe | null;Same shape as the component loaders, but for the multi-component recipes (auth-form, settings-page, command-palette, etc.). Pass results into `buildAppContext`'s optional `recipes` field to bundle them into the LLM payload.
import { loadRecipe } from "@hex-core/payload";
const auth = loadRecipe("auth-form");
console.log(auth?.components); // ["button", "input", "label", ...]getTheme / listThemes
function getTheme(name: string): Theme | undefined;
function listThemes(): readonly { name: string; theme: Theme }[];Re-exports of the theme catalog from `@hex-core/tokens`. Convenient when you already have `@hex-core/payload` installed and don't want to add a second dependency just to look up a theme by name.
import { getTheme, listThemes } from "@hex-core/payload";
const theme = getTheme("midnight") ?? getTheme("default");
console.log(listThemes().map((t) => t.name));resolveSpec
function resolveSpec(brief: string): {
components: string[];
recipes: string[];
rationale: string;
};Deterministic brief-to-shortlist resolver. Pass a natural-language description ("settings page with profile, billing, notifications"); get back a ranked list of registry slugs + rationale. Same logic the MCP `resolve_spec` tool wraps.
import { resolveSpec } from "@hex-core/payload";
const result = resolveSpec("confirm-destructive dialog with checkbox and dual-action footer");
console.log(result.components); // ["dialog", "checkbox", "button"]
console.log(result.recipes); // ["confirm-destructive"]Workflows
Render the canonical payload from a Next.js Server Component
This is what the studio's `/copy` page does. No subprocess, no JSON-RPC, no MCP transport — direct function call.
// app/llm-context/page.tsx
import { buildAppContext, loadRegistryItem } from "@hex-core/payload";
import { defaultTheme } from "@hex-core/tokens";
export default function LlmContextPage() {
const components = ["button", "card", "input", "dialog"].map((slug) => ({
slug,
item: loadRegistryItem(slug),
}));
const markdown = buildAppContext({
theme: { requested: "default", resolved: defaultTheme },
components,
});
return <pre>{markdown}</pre>;
}Generate a Figma tokens file in a CI script
Run on every theme change to keep design and code in sync.
// scripts/sync-figma-tokens.ts
import { buildFigmaTokens, getTheme } from "@hex-core/payload";
import { writeFileSync } from "node:fs";
const theme = getTheme(process.env.THEME ?? "default");
if (!theme) throw new Error("Unknown theme");
writeFileSync(
"./design/figma-tokens.json",
JSON.stringify(buildFigmaTokens(theme), null, 2),
);
console.log("Wrote figma-tokens.json");Compatibility
- Pure ESM. Server-only — `loadRegistry*` and `loadRecipe*` perform synchronous `fs` reads. Do not import from a Client Component.
- Bundles `registry/` data into the published tarball. Works in fresh `npm install` outside the hex-core monorepo.
- Depends on `@hex-core/registry@^0.2.1` and `@hex-core/tokens@^1.2.0`. Re-exports `Theme`, `RegistryItem`, `Recipe` types so consumers don't need a second dep.