# Copybook > Define the code snippets and agent prompts in your product as typed, tested, versioned content. Copybook turns the code snippets and coding-agent prompts in your UI into typed, versioned definitions. It tests every example, measures prompts against real agents, and tracks where each one is shown and when it changed. ## Set it up with a coding agent Paste this prompt into your coding agent: ```text Set up Copybook in this project. Read https://copybook.dev/docs/quick-start.md first. Every docs page is listed at https://copybook.dev/llms.txt. 1. Run npx @copybook/cli init. It writes copybook.config.ts, installs @copybook/core and @copybook/cli, and sets up coding agents: an AGENTS.md section, the Copybook skill, and the copybook MCP server in .mcp.json. 2. Find the code snippets, shell commands, and coding-agent prompts that the UI shows to users. Move each one into a definition with defineSnippet, defineCommand, or definePrompt. Give each a stable dotted id, typed params with example values, and the placements where it's shown. 3. Render the definitions where the strings used to be, with the same props. 4. Run npx copybook snapshot, then npx copybook check. Both must pass. npx copybook status --json lists anything left to do. 5. Report which definitions you created, where they're shown, and anything you couldn't migrate. ``` ## Set it up yourself ```bash npx @copybook/cli init ``` `init` writes `copybook.config.ts`, installs `@copybook/core` and `@copybook/cli`, and sets up coding agents: an `AGENTS.md` section, the Copybook skill, and the `copybook` MCP server. Then run `npx copybook status --json`: its `next` field lists what to do, with the command and MCP tool for each step. ## Every command Every command except `report` takes `--json`. Exit codes: 0 when everything is fine, 1 when a command found problems, 2 when it couldn't run. - `copybook init [options]` (ask first): Set up Copybook: write copybook.config.ts, install the packages, and set up coding agents. Safe to run again. It changes only what's out of date: copybook.config.ts, an AGENTS.md section, the Copybook skill, MCP configs, a .gitignore entry, and missing packages. --dry-run shows what would change. - `copybook status [options]` (agents can run it): Show the project's definitions, snapshots, issues, and latest results, and what to do next. - `copybook list [options]` (agents can run it): List every definition, with its kind and placements. - `copybook show [options] ` (agents can run it): Show one definition: where it's shown, its params, its rendered output, tests, and history. - `copybook snapshot [options]` (agents can run it): Write a rendered snapshot of every definition. --prune deletes snapshot files; ask before using it. - `copybook check [options]` (agents can run it): Validate definitions and fail if any snapshot is missing, stale, or orphaned. - `copybook test [options]` (agents can run it): Check that commands and snippets parse, typecheck, and run. The syntax and typecheck tiers are safe. The run tier executes commands for real, with fixtures; ask before running it. - `copybook eval [options]` (CI only): Give prompts to coding agents and check what they did. Agents run with their permission prompts bypassed, in a directory that isn't isolated. It refuses to run unless CI=true or --unsafe-local is set. - `copybook usages [options]` (agents can run it): Show which product files import each definition. - `copybook report [options]` (agents can run it): Print a Markdown summary of changed definitions, for pull request comments. - `copybook studio [options]` (ask first): Browse, preview, and trace every definition in a local UI. It keeps running. With --json it prints its URLs as one line once it's listening; its API is described at /llms.txt. - `copybook mcp [options]` (agents can run it): Serve this project to coding agents over MCP, on stdio. Your agent's MCP client starts it. Your agent's MCP client starts it over stdio. Its tools read, render, check, write snapshots, and run the safe tests; evals and the run tier stay in the CLI. - `copybook commands [options]` (agents can run it): List every command, with its flags and whether agents can run it without asking. ## What Copybook Studio shows `npx copybook studio` opens a local UI at http://127.0.0.1:4983 for browsing every definition, editing its params, and previewing the output. The Studio window on https://copybook.dev shows the Acme example project (`examples/acme` in the repository), with 8 definitions: - `console.create-project.agent` (prompt): Set up Acme with your coding agent. Shown at console/create-project/done; embeds acme.cli.install, acme.cli.login, acme.cli.init, acme.cli.deploy; defined in `src/create-project.ts#setUpWithAgent`. - `console.connect.branch` (snippet): Connect to your branch. Shown at console/branch/connect-dialog; embeds acme.cli.deploy; defined in `src/connect.ts#connect`. - `ai-gateway.model.try-it` (snippet): Try it out. Shown at console/ai-gateway/model; defined in `src/ai-gateway.ts#tryIt`. - `acme.cli.install` (command): Install the Acme CLI. Defined in `src/commands.ts#install`. - `acme.cli.login` (command): Log in. Defined in `src/commands.ts#login`. - `acme.cli.init` (command): Link the project folder. Defined in `src/commands.ts#init`. - `acme.cli.deploy` (command): Provision services and pull env vars. Defined in `src/commands.ts#deploy`. - `acme.connect.psql` (command): Passwordless connect with psql. Shown at console/projects/footer; defined in `src/commands.ts#psql`. ## Connect a coding agent `copybook init` writes everything an agent needs: a Copybook section in `AGENTS.md`, the Copybook skill in `.agents/skills/copybook`, and the `copybook` MCP server in `.mcp.json`. The server runs in your project with `npx --no copybook mcp`, and its tools are `status`, `find_definitions`, `get_definition`, `render`, `check`, `write_snapshots`, `prune_snapshots`, `run_tests`. To add it yourself: ```bash claude mcp add copybook -- npx --no copybook mcp codex mcp add copybook -- npx --no copybook mcp ``` Or in any MCP JSON config, such as `.cursor/mcp.json`: ```json { "mcpServers": { "copybook": { "args": [ "--no", "copybook", "mcp" ], "command": "npx" } } } ``` The full guide: https://copybook.dev/docs/guides/agents.md ## Read the docs over MCP https://copybook.dev/mcp is an MCP server with `list_pages`, `get_page`, and `search` tools for these docs. Add it to Claude Code with: ```bash claude mcp add --transport http copybook-docs https://copybook.dev/mcp ``` Or to any agent that reads an MCP JSON config: ```json { "mcpServers": { "copybook-docs": { "url": "https://copybook.dev/mcp" } } } ``` ## Learn more - Quick start: https://copybook.dev/docs/quick-start.md - CLI reference: https://copybook.dev/docs/reference/cli.md - Every docs page, for agents: https://copybook.dev/llms.txt - Every docs page in one file: https://copybook.dev/llms-full.txt - shadcn registry: https://copybook.dev/r/registry.json - Source: https://github.com/neon-solutions/copybook # Changelog (https://copybook.dev/docs/changelog) ## Unreleased [#unreleased] - **`@copybook/cli`**: loading the same project from two processes at once, such as Studio and `check`, no longer fails now and then with "must default-export defineConfig". The bundle cache is written atomically. - **`@copybook/cli`**: exit codes now say what happened: 0 when everything is fine, 1 when a command found problems, and 2 when it couldn't run (no config, a bad option, content that fails to load, or `eval` outside CI). Scripts that check for a non-zero code are unaffected. - **`@copybook/cli`**: with `--json`, a failure prints `{ ok: false, error: { code, message }, next }` on stdout, and errors without `--json` end with the next step to run. - **`@copybook/cli`**: `copybook init` sets up coding agents too. It adds a Copybook section to `AGENTS.md` (and to `CLAUDE.md` when that doesn't import `AGENTS.md`), writes the Copybook skill to `.agents/skills/copybook` and `.claude/skills/copybook`, registers the MCP server in `.mcp.json` (and `.cursor/mcp.json` or `.vscode/mcp.json` when those exist), ignores `.copybook/`, and prints the Codex command. `--no-agent` skips it, and `--dry-run` shows what would change. - **`@copybook/cli`**: `copybook init` is safe to run again. An existing config is kept, packages already in `package.json` aren't installed again, and agent files only change between their markers or keys. - **`@copybook/cli`**: `copybook mcp` serves the project to coding agents over MCP, on stdio. Its tools are `status`, `find_definitions`, `get_definition`, `render`, `check`, `write_snapshots`, `prune_snapshots`, and `run_tests` (syntax and typecheck), each annotated as read-only or writing, and its instructions tell the agent where to start. It starts before `copybook init`, keeps stdout for the protocol, follows file changes, and exits when the client closes stdin. - **`@copybook/cli`**: `copybook status` shows the config, the definitions, snapshot states, issues, the latest test and eval results, and what to do next. `status --json` is where agents start, and `copybook --help` says so. - **`@copybook/cli`**: `copybook show ` prints one definition as Markdown: where it's declared and rendered, what it embeds and what embeds it, its params, its output for every variant, its latest test and eval results, and its snapshot history. `--values` renders with your own values, `--text` prints just the output, and `--json` everything. Old ids resolve through aliases, and typos get suggestions. - **`@copybook/cli`**: `copybook commands --json` describes every command and flag, with what each one does to the project and whether an agent can run it without asking. The same catalog ships as `@copybook/cli/catalog.json`. - **`@copybook/cli`**: Studio's API is for agents too. `GET /api` lists every endpoint, `/llms.txt` explains them, and `/` serves that guide to requests that prefer Markdown. New endpoints: `/api/status`, `/api/commands`, `/api/definitions/{id}`, and `/api/definitions/{id}.md`. Errors are `{ error: { code, message }, next }`, with 400, 404 (with suggestions), 405, 413, 415, and 422 instead of 500. `studio --json` prints its URLs as one line once it's listening. - **`@copybook/cli`**: Studio has an Agents view: what to do next, with a Run button for writing snapshots and the safe tests, the commands to connect Claude Code, Codex, or any MCP client, and every CLI command with whether agents can run it. Each definition has a Copy for agent button that copies it as Markdown, and warnings show next to errors. - **`@copybook/cli`**: Studio can write snapshots (`POST /api/snapshot`) and run the syntax and typecheck tests (`POST /api/test`). Tests run in the background: the request answers 202 with a `runId`, results stream as `test` and `test-done` events, and a second run waits with 409. The run tier and evals stay in the CLI, and both writes are off when Studio listens beyond localhost. - **`@copybook/cli`**: Studio rejects requests for hosts other than localhost, which blocks DNS rebinding, and POSTs that aren't JSON or come from another site. It warns when `--host` exposes it to other machines. - **`@copybook/cli`**: Studio no longer reloads when tests write their results, and reloads run one at a time, so an older load can't replace a newer one. - **`@copybook/cli`**: every command except `report` takes `--json`. The output has `schemaVersion`, `ok`, and `next`: the steps to take, each with its command, MCP tool, affected ids, and whether an agent can take it without asking. `check --json` includes the snapshot diffs. - **`@copybook/cli`**: `list --grep ` finds the definitions whose id, title, placements, tags, or rendered output contain the text. `list --json` adds each definition's snapshot state, embedders, and rendering files, plus the validation issues. - **`@copybook/cli`**: `test` and `eval` warn about `--only` ids that match nothing, and human output ends with the next steps. - **`@copybook/cli`**: `test --json` is short for `--reporter json`, and `--reporter` rejects unknown values instead of falling back to JSON. - **`@copybook/cli`**: colors follow the output. They're off when stdout isn't a terminal, even when `CI` is set, and with `--json` or `NO_COLOR`. - **`@copybook/cli`**: `runCli`, `CopybookError`, and the project analysis behind `check` (`analyzeProject`, `checkIssues`, `findDefinition`, `searchDefinitions`) are exported. Every `Issue` has a `code`. ## 0.1.1 [#011] Released November 2, 2026. - **`@copybook/cli`**: `test` and `eval` no longer crash with `EPIPE` when a command exits before reading its input. - Both packages link to copybook.dev and the issue tracker from npm. Their READMEs link to the docs and describe the 0.x compatibility policy. - **`@copybook/cli`**: `copybook --help` and `copybook init` link to the docs at copybook.dev. ## 0.1.0 [#010] Released November 2, 2026. The first release. - **`@copybook/core`**: - `defineCommand`, `defineSnippet` (with dimensioned variants), `definePrompt`, and `defineCollection` - the `p` param builders, with examples, defaults, secrets, and Standard Schema - the `t` template tag, with param nodes and `t.cmd`, `t.code`, and `t.include` embeds - `render`, `renderExample`, `listVariants`, `toText`, `toSegments`, `collectCommands`, and `collectEmbeds` - **`@copybook/cli`**: - `init`, `list`, `snapshot`, and `check`, with rendered Markdown snapshots and `--changed` change detection - `test`, with syntax, typecheck, and run tiers, fixtures, and JSON and JUnit reports - `eval`, with agent adapters (`literal`, `claude-code`, `codex`), PATH shims, assertions, trials, budgets, and history - `usages` and `report`, which show where product code renders each definition and summarize pull request changes - `studio`, a local UI for browsing, previewing, and tracing definitions, with test and eval results - **Registry**: Agent Prompt, Code Variants, Command Tabs, Copy Button, and Rendered Text. # Agent Prompt (https://copybook.dev/docs/components/agent-prompt) _Interactive preview: https://copybook.dev/docs/components/agent-prompt_ This is the Acme "Create project" prompt from `examples/acme`. Edit the params to see them update. ## Installation [#installation] ```bash npx shadcn@latest add https://copybook.dev/r/agent-prompt.json ``` ## Usage [#usage] ```tsx import { render } from "@copybook/core"; import { AgentPrompt } from "@/components/copybook/agent-prompt"; import { setUpWithAgent } from "@/content/create-project"; export const CreateProjectDone = ({ orgId, projectId }) => ( track("prompt_copied", { id })} rendered={render(setUpWithAgent, { orgId, projectId })} /> ); ``` If the copied text would contain a real secret, rather than its placeholder, the component shows a warning under the prompt. ## Props [#props] | Prop | Type | Description | | --- | --- | --- | | `rendered` | `Rendered` | The rendered prompt. | | `title` | `string` | The header label. Defaults to `"Agent prompt"`. | | `onCopy` | `(event: CopyEvent) => void` | Called with `{ id, text }` after a copy. | # Code Variants (https://copybook.dev/docs/components/code-variants) ## Connect dialog [#connect-dialog] The Acme "Connect to your branch" dialog: a tab per service, each with an `.env` and an `acme.ts + cli` setup. Both setups come from a single snippet with `service` and `setup` dimensions. _Interactive preview: https://copybook.dev/docs/components/code-variants_ ## Try it out [#try-it-out] The AI Gateway model page. The model is a param, and the scenario and framework are dimensions. _Interactive preview: https://copybook.dev/docs/components/code-variants_ ## Installation [#installation] ```bash npx shadcn@latest add https://copybook.dev/r/code-variants.json ``` ## Usage [#usage] Render each variant you show on the server, then pass them in: ```tsx import { listVariants, render } from "@copybook/core"; import { CodeVariants } from "@/components/copybook/code-variants"; import { tryIt } from "@/content/ai-gateway"; export const TryIt = ({ endpoint, model }) => ( variant.when.scenario === "chat") .map((variant) => ({ key: variant.key, label: variant.label, rendered: render(tryIt, { endpoint, model }, { variant: variant.key }), }))} /> ); ``` ## Props [#props] | Prop | Type | Description | | --- | --- | --- | | `items` | `{ key, label, rendered }[]` | One tab each, in order. | | `defaultKey` | `string` | The tab that starts selected. | | `onCopy` | `(event: CopyEvent) => void` | Called with `{ id, variant, text }` after a copy. | # Command Tabs (https://copybook.dev/docs/components/command-tabs) The install commands on this site use Command Tabs. A tab can also hold an agent prompt, like the first tab here: Paste this prompt into your coding agent: ```text Set up Copybook in this project. Read https://copybook.dev/docs/quick-start.md first. Every docs page is listed at https://copybook.dev/llms.txt. 1. Run npx @copybook/cli init. It writes copybook.config.ts, installs @copybook/core and @copybook/cli, and sets up coding agents: an AGENTS.md section, the Copybook skill, and the copybook MCP server in .mcp.json. 2. Find the code snippets, shell commands, and coding-agent prompts that the UI shows to users. Move each one into a definition with defineSnippet, defineCommand, or definePrompt. Give each a stable dotted id, typed params with example values, and the placements where it's shown. 3. Render the definitions where the strings used to be, with the same props. 4. Run npx copybook snapshot, then npx copybook check. Both must pass. npx copybook status --json lists anything left to do. 5. Report which definitions you created, where they're shown, and anything you couldn't migrate. ``` Or run `init` yourself, with your package manager: ```bash pnpm dlx @copybook/cli init # pnpm npx @copybook/cli init # npm yarn dlx @copybook/cli init # yarn bunx @copybook/cli init # bun ``` Install it: ```bash npx shadcn@latest add https://copybook.dev/r/command-tabs.json ``` ## Usage [#usage] Make the package manager a param, then render one tab per value: ```tsx import { render } from "@copybook/core"; import { CommandTabs } from "@/components/copybook/command-tabs"; import { init, setupPrompt } from "@/content/definitions/get-started"; const managers = ["pnpm", "npm", "yarn", "bun"] as const; export const GetStarted = () => ( ({ id: packageManager, label: packageManager, rendered: render(init, { packageManager }), })), ]} /> ); ``` ## Props [#props] | Prop | Type | Description | | --- | --- | --- | | `tabs` | `{ id, label, rendered, kind? }[]` | One tab each, in order. `kind: "prompt"` wraps long lines and adds an agent icon. | | `defaultTab` | `string` | The tab that starts selected. | | `onCopy` | `(event: CopyEvent) => void` | Called with `{ id, text }` after a copy. | # Overview (https://copybook.dev/docs/components) The components are distributed as a [shadcn registry](https://ui.shadcn.com/docs/registry). You own the code, so restyle it to match your product. Each component takes a `Rendered` tree, not a definition. Call `render()` on the server and pass the result to the component. Only the variants you show reach the browser, and the tree is plain JSON. | Component | Use it for | | --- | --- | | [Agent Prompt](/docs/components/agent-prompt) | A prompt to paste into a coding agent | | [Code Variants](/docs/components/code-variants) | One snippet in several frameworks or setups | | [Command Tabs](/docs/components/command-tabs) | A command per package manager, with an optional prompt tab | Every component: - highlights params, and shows secret placeholders differently from real values; - sets `data-copybook-id` (and `data-copybook-variant`) on its root, so you can find definitions on a live page; - calls `onCopy({ id, variant, text })` after a copy, for analytics; - depends only on shadcn's `button`, `tabs`, and `utils`, plus `@copybook/core`. # Definitions (https://copybook.dev/docs/concepts/definitions) Everything your product shows is a **definition**. Every definition has a stable `id`, typed `params`, and metadata that the CLI catalogs. | Kind | Helper | Example | | --- | --- | --- | | Command | `defineCommand` | `neon init --project-id ` | | Snippet | `defineSnippet` | An AI SDK example in TypeScript, Python, and cURL | | Prompt | `definePrompt` | A "set up this app" prompt for a coding agent | ## Metadata [#metadata] All three helpers accept the same metadata: ```ts defineCommand({ id: "neon.init", // required: lowercase, dotted, never reused title: "Initialize a project", description: "Links the current folder to a Neon project.", placements: ["console/create-project"], // where the product shows it tags: ["cli"], owners: ["@devrel"], aliases: ["neon.link"], // previous ids, so history follows a rename template: (t) => t`neon init`, }); ``` ### Ids [#ids] Ids are how everything else refers to a definition: snapshots, test results, eval history, and analytics. Use the form `area.surface.name`, keep ids lowercase, and never reuse one. When you rename a definition, add the old id to `aliases`. ## Commands [#commands] A command is one shell command. Its `verify` option tells the [test runner](/docs/guides/testing) how far to go: ```ts import { defineCommand, skip } from "@copybook/core"; export const login = defineCommand({ id: "neon.login", template: (t) => t`neon login`, verify: skip("interactive: opens a browser"), }); ``` | `verify` | What the runner does | | -------------------- | -------------------------------------------------- | | `"syntax"` (default) | Parses it without running it | | `"typecheck"` | Installs dependencies and typechecks it (snippets) | | `"run"` | Runs it in a sandbox with fixture values | | `skip(reason)` | Never runs it, and records why | ## Snippets [#snippets] A snippet is a code block. Give it a `lang` and a `template`, or static `code`: ```ts import { defineSnippet } from "@copybook/core"; export const env = defineSnippet({ id: "acme.connect.env", lang: "dotenv", code: "DATABASE_URL=postgres://user:password@host/db", }); ``` When one snippet comes in several languages or frameworks, declare [variants](/docs/concepts/variants). ## Prompts [#prompts] A prompt is text for a coding agent. It usually embeds commands and snippets: ```ts export const getStarted = definePrompt({ id: "console.create-project.agent", template: (t) => t` 1. ${t.cmd(install)} 2. ${t.cmd(login)} `, }); ``` Embedded definitions keep their ids in the rendered tree. That's how the CLI knows that `neon.login` appears in this prompt, and how the test runner finds commands to run. ## Collections [#collections] Sometimes definitions come from data, such as one snippet per model. `defineCollection` builds them with ids derived from a stable key: ```ts import { defineCollection, defineSnippet } from "@copybook/core"; export const modelPages = defineCollection({ idPrefix: "ai-gateway.model", items: models, key: (model) => model.slug, // never the array index build: (model, { id }) => defineSnippet({ id, lang: "ts", template: (t) => t`...` }), }); modelPages.get("claude-haiku-5-5"); // id: ai-gateway.model.claude-haiku-5-5 ``` > **Prefer params over collections.** If only one string changes between items, such as a model id, make it a param instead of generating a definition per item. See [Variants](/docs/concepts/variants). # Params (https://copybook.dev/docs/concepts/params) Params are the values that change between users: org ids, project ids, endpoint URLs, and tokens. Declare them with the `p` builders. ```ts import { p } from "@copybook/core"; const params = { projectId: p.string().example("proj-example").describe("The Neon project id"), branch: p.string().default("main"), model: p.enum(["claude-haiku-5-5", "gpt-5-mini"]).example("claude-haiku-5-5"), token: p.secret({ env: "NEON_API_KEY", placeholder: "" }), verbose: p.boolean().optional(), }; ``` | Builder | Value type | | --- | --- | | `p.string()`, `p.url()` | `string` | | `p.number()` | `number` | | `p.boolean()` | `boolean` | | `p.enum([...])` | One of the given strings | | `p.secret({ placeholder, env? })` | `string` | | `p.schema(schema)` | The output type of any [Standard Schema](https://standardschema.dev) (Zod, Valibot, ArkType) | Every builder is immutable and chainable: - `.example(value)`: used for previews, snapshots, and Studio. - `.default(value)`: used when `render` gets no value. It also makes the param optional. - `.optional()`: the param may be missing, so it's `undefined` in the template. - `.describe(text)`: shown in Studio and the manifest. ## Examples are required [#examples-are-required] Every required param needs an `.example()` or a `.default()`. Without one, the CLI can't render a preview or a snapshot, and `copybook check` fails. ## Secrets [#secrets] `p.secret()` is always optional at render time. Without a value, it renders its placeholder, so real credentials only appear when you pass them explicitly: ```ts render(snippet); // Authorization: Bearer render(snippet, { token }); // Authorization: Bearer sk-… ``` In the rendered tree, secret params carry `secret: true`, and placeholders also carry `placeholder: true`. Test runs read the real value from `env` and redact it from all logs. ## Highlighting [#highlighting] When you interpolate a param directly into a `t` template, it becomes a **param node**, so UIs can highlight it. If you transform the value first, as in `` t`${projectId.toUpperCase()}` ``, it renders as plain text. # Snapshots (https://copybook.dev/docs/concepts/snapshots) `copybook snapshot` renders every definition with its example values and writes one Markdown file per definition: ```md title="copybook/snapshots/console.create-project.agent.md" --- id: console.create-project.agent kind: prompt title: "Set up Acme with your coding agent" source: src/create-project.ts#setUpWithAgent placements: - console/create-project/done params: orgId: org-summer-dust-12345678 projectId: round-bar-12345678 embeds: - acme.cli.install - acme.cli.init hash: sha256:a7bcdda7487fd64a --- Set up this app on Acme. 1. npm i -g acme@latest. If that fails, use `npx acme@latest` in place of `acme`. … ``` Snapshots with variants get a `## ` section per variant. ## Why snapshots [#why-snapshots] Snapshots track **output**, not source: - **Changes show up where they land.** Change `acme.cli.deploy` and the diff shows every prompt and snippet that embeds it, not just the one line you edited. - **Reviewers see what users see.** DevRel and docs reviewers can review a prompt change without reading TypeScript. - **"When did this change?" is `git log`.** Every snapshot file has its own history, including authors and pull requests. Studio shows it per definition. - **Ownership is a CODEOWNERS rule.** For example: `copybook/snapshots/console.* @devrel`. ## Check in CI [#check-in-ci] ```bash copybook check ``` `check` fails when a snapshot is missing, out of date (it prints the diff), or orphaned. It also validates the definitions: - every required param has an example or a default; - templates render the same output twice, so they're pure; - placements are listed in the config, if you list them; - aliases don't collide with ids. Pass `--base origin/main` to also warn when an id disappears without being kept as an alias. ## Only what changed [#only-what-changed] ```bash copybook list --changed --base origin/main ``` This lists the definitions whose rendered output differs from the base ref, including ones that aren't snapshotted yet. Tests and evals use the same list, so a pull request only runs what it changed. > **Keep formatters away from snapshots.** Snapshots must stay byte-for-byte what `copybook snapshot` wrote. Add the snapshot folder to your formatter's ignore list, for example `**/copybook/snapshots/**` in Prettier, Biome, or oxfmt. # Templates (https://copybook.dev/docs/concepts/templates) Every definition has a `template` function. It receives the `t` tag and the param values, and returns a fragment. ```ts template: (t, { projectId }) => t`acme init --project-id ${projectId}`; ``` ## Dedent [#dedent] Templates are dedented, and a blank first or last line is dropped, so a template can open and close on its own lines: ```ts template: (t) => t` First line. Indented line. `; // "First line.\n Indented line." ``` ## Interpolations [#interpolations] | Value | Renders as | | --- | --- | | A param value | A param node | | A string or number | Text | | `t.cmd(...)`, `t.code(...)`, `t.include(...)` | Embedded nodes | | An array | Each item, in order | | `null`, `undefined`, `true`, `false` | Nothing, so `${flag && t.cmd(...)}` works | ## Embedding [#embedding] - `t.cmd("npm test")` or `t.cmd(commandDefinition, values)` embeds a command. - `t.code("ts", code)` or `t.code(snippetDefinition, values, { variant })` embeds a code block. - `t.include(definition, values)` embeds another definition, such as a shared closing section for prompts. Embedding a definition keeps its id in the rendered tree: ```ts const tree = render(getStarted, { projectId: "proj-1" }); collectEmbeds(tree); // ["acme.install", "acme.init"] collectCommands(tree); // [{ id: "acme.init", text: "acme init --project-id proj-1" }, ...] ``` ## Indentation [#indentation] A multi-line embed is indented to the column of the line it starts on. That keeps a code block nested under a list item: ```ts template: (t) => t` 3. Create neon.ts: ${t.code(neonTs)} `; ``` ````md 3. Create neon.ts: ```ts export default defineConfig({ … }); ``` ```` ## Text output [#text-output] `render` returns a serializable `Rendered` tree. Turn it into text with: - `toText(tree)`: the text users copy. By default, embedded code blocks get Markdown fences. Pass `{ format: "plain" }` to leave them out. - `toSegments(tree)`: flat segments (`text`, `param`, `fence`), each with the id and block it came from. Use these to render highlighted UIs. ``` ``` # Variants (https://copybook.dev/docs/concepts/variants) A snippet that comes in several forms declares **dimensions**, plus one entry in `variants` for each combination you show: ```ts import { defineSnippet, p } from "@copybook/core"; export const chat = defineSnippet({ id: "ai-gateway.try-it.chat", placements: ["console/ai-gateway/model"], params: { model: p.enum(MODELS).example("claude-haiku-5-5"), token: p.secret({ env: "NEON_AI_GATEWAY_TOKEN", placeholder: "", }), }, dimensions: { framework: [ { value: "ai-sdk", label: "AI SDK" }, { value: "python", label: "Python" }, { value: "curl", label: "cURL" }, ], }, variants: [ { when: { framework: "ai-sdk" }, lang: "ts", deps: { npm: ["ai", "@neondatabase/ai-sdk-provider"] }, template: (t, { model }) => t` const { text } = await generateText({ model: neon("${model}"), prompt: "Hi" }); `, }, { when: { framework: "curl" }, lang: "bash", template: (t, { token }) => t`curl -H "Authorization: Bearer ${token}" …`, }, ], }); ``` Options are an array of `{ value, label }`, in display order. An array keeps that order even under lint rules that sort object keys. ## Keys [#keys] Each variant gets a canonical key, with dimensions in declaration order: `framework=ai-sdk`, or `framework=ai-sdk;setup=env` when there are several dimensions. Select one when rendering, by key or by dimensions: ```ts render(chat, values, { variant: { framework: "curl" } }); render(chat, values, { variant: "framework=curl" }); ``` Without a selection, the first variant renders. Matrices can be sparse: declare only the combinations you show. ## Avoiding the matrix explosion [#avoiding-the-matrix-explosion] The Neon AI Gateway shows _models × scenarios × frameworks_. Model it like this: - **Model is a param**, not a dimension. Only one string changes between models. - **Scenario and framework are dimensions.** Their code really is different. - **Use a collection** only when per-item content truly differs. See [Collections](/docs/concepts/definitions#collections). That turns hundreds of near-copies into about 15 hand-written variants. ## Dependencies [#dependencies] `deps` lists what a variant needs to run. The test runner installs these packages into its sandbox before typechecking or running the variant. # Coding agents (https://copybook.dev/docs/guides/agents) Copybook is built to be driven by coding agents as much as by people. Every surface tells an agent what exists, what needs doing, and which command or tool does each step, and the rules about what an agent may do without asking are explicit. > **Version 0.2.0.** Everything on this page except the docs MCP server needs `@copybook/cli` 0.2.0 or later. ## Set up [#set-up] `copybook init` sets up coding agents along with the config. Run it in a new project with `npx @copybook/cli init`, or again in a project that already uses Copybook. | File | What it holds | | --- | --- | | `AGENTS.md` | A Copybook section between `` and ``. Codex, Cursor, and most other agents read this file. | | `CLAUDE.md` | The same section, only when the file exists and doesn't import `AGENTS.md`. | | `.agents/skills/copybook/SKILL.md`, `.claude/skills/copybook/SKILL.md` | The Copybook skill: when to use it, the workflow, and the rules. | | `.mcp.json` | The `copybook` MCP server, for Claude Code. | | `.cursor/mcp.json`, `.vscode/mcp.json` | The same server, when the project already uses Cursor or VS Code. | | `.gitignore` | `.copybook/`, where test and eval results go. | Running `init` again only changes what's out of date, and leaves your other MCP servers and the rest of `AGENTS.md` alone. `--dry-run` shows the plan, and `--no-agent` skips these files. Codex keeps MCP servers in its own config, so `init` prints the command to add it: ```bash codex mcp add copybook -- npx --no copybook mcp ``` To add only the skill, run `npx skills add neon-solutions/copybook`. ## Start with status [#start-with-status] ```bash npx copybook status --json ``` `status` reports the config, the definitions, snapshot states, issues, and the latest test and eval results. Its `next` field lists what to do, in order: ```json { "argv": ["snapshot"], "code": "write-snapshots", "command": "copybook snapshot", "ids": ["acme.cli.init"], "safe": true, "title": "Write the missing and changed snapshots, then review and commit them.", "tool": "write_snapshots" } ``` `clean` is true when there's nothing to do, and `runner` says how to run the CLI in the project, such as `pnpm exec copybook`. ## The CLI [#the-cli] Every command except `report` takes `--json`, with the same `next` steps and an error envelope when it fails. Exit codes are 0 when everything is fine, 1 when a command found problems, and 2 when it couldn't run. See [JSON output and exit codes](/docs/reference/cli#json-output-and-exit-codes). The commands agents use most: ```bash copybook list --grep "npm install" # which definition renders this text? copybook show # everything about one definition, as Markdown copybook snapshot # after editing definitions copybook check --json # what's wrong, with diffs copybook test --json # syntax and typecheck tests copybook commands --json # every command and flag ``` ## MCP [#mcp] `copybook mcp` serves the project over stdio. Its instructions tell the agent to start with `status`. | Tool | What it does | | --- | --- | | `status` | What needs doing, with the tool for each step. | | `find_definitions` | Searches ids, titles, placements, tags, and rendered output. Filters by kind, placement, unused, or changes since a ref. | | `get_definition` | One definition as Markdown, like `copybook show`. | | `render` | Renders with values, for every variant or one. | | `check` | Issues and snapshot diffs, like `copybook check`. | | `write_snapshots` | Writes the missing and changed snapshots. | | `prune_snapshots` | Deletes snapshots without a definition. Marked destructive. | | `run_tests` | The syntax and typecheck tests. | The server starts before `copybook init`, so its tools can say to run it, and follows file changes while it runs. ## Studio [#studio] `copybook studio` has an Agents view with the same next steps, a Run button for the safe ones, the commands to connect each agent, and every CLI command. Each definition has a Copy for agent button that copies it as Markdown. Agents can use Studio's API directly. `copybook studio --json --port 0` prints one line with its URLs, `/llms.txt` describes every endpoint, and `GET /api/status` is the place to start. See [Studio](/docs/guides/studio#api). ## The docs [#the-docs] These docs are written for agents too: - [/llms.txt](/llms.txt) lists every page, and [/llms-full.txt](/llms-full.txt) has all of them in one file. - Every page has a Markdown version at its URL plus `.md`, and requests that prefer `text/markdown` get it, the landing page included. - `https://copybook.dev/mcp` is an MCP server with `list_pages`, `get_page`, and `search` tools. Add it to Claude Code with `claude mcp add --transport http copybook-docs https://copybook.dev/mcp`. ## What agents may do [#what-agents-may-do] - **Without asking**: read everything, render, write snapshots, and run the syntax and typecheck tests, from the CLI, MCP, or Studio. - **Ask first**: `snapshot --prune` deletes files, `test --tier run` executes commands for real, and `init` installs packages. - **CI only**: `eval` gives prompts to agents with their permission prompts bypassed. See [Evals](/docs/guides/evals). Studio only takes writes from localhost: it rejects other hosts and cross-site requests, and it's read-only when it listens beyond `127.0.0.1`. # CI (https://copybook.dev/docs/guides/ci) ## On every pull request [#on-every-pull-request] ```yaml title=".github/workflows/copybook.yml" - run: npx copybook check --base origin/main - run: npx copybook test --changed --base origin/main ``` - `check` fails when a snapshot is missing, stale, or orphaned, and warns about renames without aliases and about definitions nothing renders. - `test --changed` parses and typechecks only what the pull request changed. Add `--tier syntax,typecheck,run` once your fixtures have credentials. ## Pull request comments [#pull-request-comments] `copybook report` prints a Markdown summary of the definitions a pull request changes: - each definition's kind, placements, the files that render it, and its latest test results; - its rendered snapshot diff, collapsed. ```bash npx copybook test --changed --base origin/main npx copybook report --base origin/main > report.md gh pr comment "$PR" --body-file report.md ``` The report starts with a hidden `` marker, so a workflow can find its earlier comment and update it instead of posting a new one. The repository's `pr-report.yml` workflow does exactly that. ## Nightly [#nightly] ```bash npx copybook test --tier syntax,typecheck,run CI=true npx copybook eval --history-dir eval-history --budget-usd 10 ``` Run the `run` tier and evals on a schedule, with the credentials your fixtures need. Keep eval history in git (the repository's `evals.yml` uses an orphan `copybook-results` branch), so unchanged prompts aren't re-evaluated and pass rates have a history. See [Evals](/docs/guides/evals). ## Ownership [#ownership] Snapshots are plain files, so CODEOWNERS works on them: ```txt title=".github/CODEOWNERS" copybook/snapshots/console.* @acme/devrel copybook/snapshots/ai-gateway.* @acme/ai-platform ``` # Evals (https://copybook.dev/docs/guides/evals) A prompt can parse fine and still fail when an agent follows it. Evals run your prompts through real coding agents in a sandbox, then check the result. ```ts title="verify/create-project.verify.ts" import { defineEval, fileContains, fileExists, ranInOrder, succeeds, } from "@copybook/cli"; import { setUpWithAgent } from "../src/create-project.prompts"; export const createProjectEval = defineEval(setUpWithAgent, { agents: ["literal", "claude-code"], trials: 3, passRate: 0.67, sandbox: { template: "evals/template" }, shims: { neon: { script: "…" }, // record and stub the CLI npm: "passthrough", // record, then run the real npm }, assert: [ ranInOrder([/neon init .*--project-id/, "neon deploy"]), fileExists("neon.ts"), fileContains(".env.local", "DATABASE_URL="), succeeds("npm run build"), ], maxCostUsd: 2, timeoutMs: 10 * 60_000, }); ``` ```bash CI=true npx copybook eval --budget-usd 10 ``` ## How a trial runs [#how-a-trial-runs] 1. The `sandbox.template` directory is copied into a fresh temp directory. 2. **Shims** for the binaries you list go first on `PATH`. Every call is logged, whichever agent made it, and a shim can pass through to the real binary or stub it. 3. The prompt is rendered with `params` (and secrets from their `env` variables) and given to the agent. 4. After the agent finishes, the assertions run in the sandbox, in order. A trial passes when the agent didn't time out and every assertion holds. An eval passes when its pass rate meets `passRate`. ## Assertions [#assertions] | Assertion | Passes when | | --- | --- | | `ran(pattern)` | A shimmed call matches. Calls are matched as `bin arg1 arg2`. | | `notRan(pattern)` | No shimmed call matches. | | `ranInOrder([a, b, c])` | Calls matching each pattern happened in this order, with others allowed in between. | | `fileExists(path)` | The file exists in the sandbox. | | `fileContains(path, pattern)` | The file matches. | | `succeeds(command, { exitCode? })` | The shell command exits with `exitCode` (default `0`). | | `assertThat(name, check)` | Your own check, given the agent run, the calls, and the sandbox path. | ## Agents [#agents] | Name | Adapter | Notes | | --- | --- | --- | | `literal` | `literal()` | Runs the prompt's embedded commands in order, without an LLM. It's free and deterministic, and if the literal steps fail, no agent will succeed. | | `claude-code` | `claudeCode({ model?, args? })` | `claude -p` with `--permission-mode bypassPermissions`, `--bare`, and `--max-budget-usd` from `maxCostUsd`. It reports cost. | | `codex` | `codex({ model?, args? })` | `codex exec --json` with approvals bypassed. Check the flags against your Codex version. | Anything that implements `AgentAdapter` (`name`, `available`, `version`, and `run`) works too. `scriptAgent(name, script)` is handy for tests. ## Cost and caching [#cost-and-caching] - `--budget-usd` stops starting new trials once the run has spent that much. - `--history-dir ` appends one line per prompt and agent to `/.jsonl`, with the pass rate, cost, agent version, and the snapshot hash of the prompt. On the next run, a prompt whose text and agent version haven't changed is reported as `cached` and not re-run. Pass `--force` to re-run it anyway. - `--changed --base origin/main` only evaluates prompts whose rendered text changed. Transcripts are written to `.copybook/results/evals//`. ## Safety [#safety] > **Agents get full permissions.** Agents run with their permission prompts bypassed, inside a temp directory that isn't a container. `eval` refuses to run unless `CI=true` is set or you pass `--unsafe-local`. Use short-lived, scoped credentials in fixtures, never personal ones. ## In CI [#in-ci] The repository's `evals.yml` workflow shows a full setup: - It runs nightly and on demand, and on pull requests only when the `run-evals` label is added. - History is kept on an orphan `copybook-results` branch, so git stays the only storage. - Transcripts are uploaded as an artifact. # Modeling a console (https://copybook.dev/docs/guides/modeling-a-console) This guide walks through three real screens from the Neon Console and how to model each one. `examples/acme` implements all three for a fictional "Acme DB", and the [component docs](/docs/components) render them live. ## 1. The "Create project" agent prompt [#1-the-create-project-agent-prompt] The last step of the dialog shows a prompt for a coding agent. It mixes prose with commands: install the CLI, log in, `init` with the new project's ids, `deploy`, then prove the build works. - **Every command is a `defineCommand`**: `install`, `login`, `init`, and `deploy`. `login` uses `verify: skip("interactive: opens a browser sign-in")`. - **The prompt embeds them** with `t.cmd(init, params)`, so the org and project ids are typed params with examples. - **An eval checks that agents follow it**: `ranInOrder([/acme@latest/, /acme init/, "acme deploy"])`, then `succeeds("npm run build")` in a template app, with the CLI stubbed by shims. Because `deploy` is defined once, the drift Neon saw can't happen. Neon had one snippet telling users to run `npx neon link` while the prompt said `neon deploy`. Here, changing the command changes every snapshot that embeds it, all in the same pull request. ## 2. The "Connect to your branch" dialog [#2-the-connect-to-your-branch-dialog] Five services (Postgres, Storage, AI Gateway, Data API, Auth), each with an `.env` tab and a `neon.ts + cli` tab. - **One snippet with two dimensions**: `service` and `setup`. Only the combinations the dialog shows are declared, because sparse matrices are fine. - **Credentials are secrets**: `p.secret({ env, placeholder })`. Previews show ``. Real values only appear when the product passes them, and the components warn when a copied prompt contains one. - **The setup comments embed the deploy command** with `t.cmd(deploy)` instead of retyping it. - **Verify by language**: `.env` variants get a dotenv parse, and the OpenAI client variant is typechecked against the real `openai` package. ## 3. AI Gateway model pages [#3-ai-gateway-model-pages] Every model has a page with "Try it out": a scenario dropdown and AI SDK, Mastra, TypeScript, Python, and cURL tabs. - **The model is a param** (`p.enum(MODELS)`), not a dimension. One definition covers every model page, so you don't need one per model. - **Scenario and framework are dimensions.** Their code really differs. - **The cURL variant runs for real** in the `run` tier, against a fixture. In Acme it's a local mock gateway; for Neon it would be a temporary branch with a scoped token. ## Where things are shown [#where-things-are-shown] Each definition declares `placements`, such as `console/branch/connect-dialog`. With `usage.include` set, `copybook usages` also finds the actual files that render it, and `check` warns about definitions nothing renders, such as a footer snippet left behind after a redesign. # Studio (https://copybook.dev/docs/guides/studio) ```bash npx copybook studio ``` Studio opens on [http://127.0.0.1:4983](http://127.0.0.1:4983). It loads your config, watches your content files, and reloads when they change. Nothing is hosted or uploaded. For every definition, Studio shows: - **What it is**: its id, kind, title, description, and the file and export it comes from. - **Where it's shown**: its declared placements. - **A live preview**: edit any param and see every variant re-render, with the same registry components your product uses. - **Embeds and used-by**: which commands a prompt embeds, and which prompts embed a command. Follow the links to trace a change. - **History**: the commits that touched its snapshot, with authors and dates. - **Snapshot status**: up to date, out of date, or missing. - **Tests and evals**: the latest results from `copybook test` and `copybook eval`. - **Agents**: what to do next, with a Run button for writing snapshots and the safe tests, how to connect a coding agent over MCP, and every CLI command. Each definition also has a Copy for agent button that copies it as Markdown. | Flag | Description | | --- | --- | | `-p, --port ` | The port to listen on. Defaults to `4983`. | | `--host ` | The host to bind. Defaults to `127.0.0.1`. | | `--api-only` | Serve only the JSON API. | | `--json` | Print the URLs as one line of JSON once it's listening. | | `-c, --config ` | The config file. | ## API [#api] Studio's UI runs on a JSON API that agents and scripts can use too. `copybook studio --json --port 0` picks a free port and prints one line with `url`, `api`, and `llms` once it's listening. `/llms.txt` describes every endpoint, and `/` serves the same guide to requests that prefer Markdown. | Endpoint | What it returns | | --- | --- | | `GET /api` | Every endpoint, as JSON. | | `GET /api/status` | What needs doing, like `copybook status --json`. | | `GET /api/manifest` | Every definition, with its snapshot state, embedders, and rendering files. | | `GET /api/definitions/{id}` | One definition, like `copybook show --json`. Add `.md` for Markdown. | | `POST /api/render` | A definition rendered with `{ id, values?, variant? }`. | | `POST /api/snapshot` | Writes the missing and changed snapshots. `{ prune: true }` also deletes orphans. | | `POST /api/test` | Starts the syntax and typecheck tests and answers 202 with a `runId`. | | `GET /api/history?id={id}` | The commits that changed a definition's snapshot. | | `GET /api/results` | The latest test and eval reports. | | `GET /api/commands` | Every CLI command and flag. | | `GET /api/events` | Server-sent events: `change`, `error`, `test`, `test-done`, and `test-error`. | Test runs happen in the background: follow `/api/events`, or poll `/api/results` until `test.runId` matches. A second run while one is going answers 409. The run tier and evals stay in the CLI. Errors are `{ "error": { "code", "message" }, "next": [...] }`, with 400 for bad input, 404 for unknown ids (with suggestions), 405 for the wrong method, 413 for bodies over 1 MB, 415 for POSTs that aren't JSON, and 422 for values a definition rejects. > **Studio runs your code.** Like `snapshot` and `check`, Studio imports your content files to render them. It binds to `127.0.0.1` by default. Don't expose it on a shared network. ## Security [#security] Studio answers only requests for `localhost`, `127.0.0.1`, or `[::1]`, on any port, which blocks DNS rebinding and keeps the Vite dev proxy working. POSTs must be `Content-Type: application/json`, which forces a CORS preflight that Studio never answers, and must come from a localhost page or a local tool. With `--host` set to another address, Studio warns and turns read-only: snapshot and test requests answer 403. # Testing snippets and commands (https://copybook.dev/docs/guides/testing) ```bash npx copybook test # syntax + typecheck npx copybook test --tier syntax,typecheck,run npx copybook test --changed --base origin/main ``` ## Tiers [#tiers] Every command and snippet variant gets the tiers its `verify` option allows, capped by what its language supports. | Tier | What runs | When to use it | | --- | --- | --- | | `syntax` | `bash -n` for shell, esbuild for JS and TS, a dotenv parser, Python's `ast`, and `JSON.parse` | Every pull request. It's fast and needs nothing. | | `typecheck` | Installs the variant's `deps.npm` into a cached sandbox, then runs `tsc --strict` | Every pull request. It catches SDK API drift. | | `run` | Executes the code in a sandbox, with values from a fixture | Nightly, or for changed definitions | ```ts defineSnippet({ // … variants: [ { when: { framework: "ai-sdk" }, lang: "ts", deps: { npm: ["ai", "@neondatabase/ai-sdk-provider"] }, verify: "typecheck", template, }, { when: { framework: "curl" }, lang: "bash", verify: "run", template }, ], }); ``` Prompts don't have a `verify` option. Their embedded definitions are tested on their own, and literal commands such as `t.cmd("npm run build")` get a syntax check. To check that a prompt works, use [evals](/docs/guides/evals). Secrets never reach the syntax and typecheck tiers. They get stand-in values like `copybook_test_token`. ## Running for real [#running-for-real] The `run` tier needs real values, and usually a real resource. Put `defineTest` and `defineFixture` in files your product never imports, and list them in the config: ```ts title="copybook.config.ts" export default defineConfig({ content: ["src/**/*.prompts.ts"], verify: ["verify/**/*.verify.ts"], }); ``` ```ts title="verify/gateway.verify.ts" import { defineFixture, defineTest } from "@copybook/cli"; import { tryIt } from "../src/ai-gateway.prompts"; export const branch = defineFixture({ id: "neon.branch", setup: async ({ runId }) => { const created = await createBranch(`copybook-${runId}`); // your API client return { value: created, teardown: () => deleteBranch(created.id), }; }, }); export const tryItCurl = defineTest(tryIt, { fixture: branch, params: (created) => ({ endpoint: created.aiGatewayUrl, model: "claude-haiku-5-5", }), variants: ["framework=curl;scenario=chat"], expect: { stdout: /content/ }, timeoutMs: 60_000, }); ``` - A fixture is set up once per run, only if a selected test uses it. Its teardown always runs, even when tests fail. - Name what you create `copybook-`, so anything a crashed run leaves behind is easy to find and clean up. - Secrets that `params` doesn't provide come from each secret's `env` variable. If neither has a value, the case is skipped and the output says which variable to set. - Secret values are redacted from all output. `examples/acme` runs its cURL snippet against a local mock gateway, so the `run` tier works without any accounts. ## Sandboxes [#sandboxes] - **Environment:** only `PATH`, `HOME`, `LANG`, `TERM`, `TMPDIR`, `USER`, and npm, pip, and uv registry settings are passed through, plus the `env` that a fixture or test adds. - **npm:** installs go into `node_modules/.cache/copybook/sandboxes`, cached by dependency list, with lifecycle scripts disabled. - **Python:** dependencies are installed with `uv run --with` when `uv` is available. - **Isolation:** sandboxes are temp directories, not containers. Run the `run` tier in CI or another disposable environment. ## Reports [#reports] | Flag | Description | | --- | --- | | `-t, --tier ` | Comma-separated tiers. Defaults to `syntax,typecheck`. | | `--only ` | Only these definitions. | | `--changed`, `--base ` | Only definitions whose output changed since the ref. | | `--reporter ` | `pretty` (default), `json`, or `junit`. | | `-o, --output ` | Write the JSON or JUnit report to a file. | | `--concurrency ` | How many cases run at once. | Every run also writes `.copybook/results/test.json`, which Studio reads. `test` exits with status 1 if any case fails. # Introduction (https://copybook.dev/docs) Developer tools ship a lot of copy-paste content: "Connect" dialogs with snippets in five languages, model pages with per-framework examples, and "paste this into your coding agent" prompts. That content usually lives as strings scattered across components, so nobody can easily answer: - **What** snippets and prompts do we show, and **where**? - **Do** all the code examples still **work**? - Do the prompts still **perform well** with today's agents? - **When** did each one last change? Copybook answers these questions by making every snippet, command, and prompt a definition with a stable id, typed params, and declared placements. Your product renders definitions with real values. The CLI catalogs them, snapshots their rendered output so every change shows up in review, runs them in sandboxes, and evaluates prompts with real coding agents. > **Version 0.x.** Copybook is at 0.x, so minor releases can include breaking changes. The [changelog](/docs/changelog) lists them. ## How it fits together [#how-it-fits-together] | Question | Answered by | | --- | --- | | What exists | `copybook list`, the manifest, and Studio | | Where it's shown | Declared `placements` | | When it changed | Rendered snapshots committed next to your code | | Does it work | `copybook test`, with syntax, typecheck, and run tiers | | Does it perform well | `copybook eval`, which runs prompts through agent adapters | ## Packages [#packages] - `@copybook/core`: isomorphic, zero-dependency definitions and rendering. This is what your product imports. - `@copybook/cli`: catalog, snapshots, tests, evals, and the local Studio. - **Registry components**: shadcn components for agent prompts, code variants, and command tabs. You install them with `shadcn add`. # Quick start (https://copybook.dev/docs/quick-start) ## Install [#install] ```npm npm i @copybook/core ``` `@copybook/core` has no dependencies. It runs in the browser, on the server, and at the edge. ## Define a command [#define-a-command] A command is a shell command that your product shows to users. Its params are typed, and each one has an example value so the command can always be previewed. ```ts title="content/acme.ts" import { defineCommand, p } from "@copybook/core"; export const init = defineCommand({ id: "acme.init", params: { projectId: p.string().example("proj-example"), branch: p.string().default("main"), }, template: (t, { projectId, branch }) => t`acme init --project-id ${projectId} --branch ${branch}`, }); ``` ## Define a prompt [#define-a-prompt] A prompt is text that users paste into a coding agent. Embed commands with `t.cmd()` instead of retyping them. When the command changes, every prompt that embeds it changes too. ```ts title="content/create-project.ts" import { definePrompt, p } from "@copybook/core"; import { init } from "./acme"; export const getStarted = definePrompt({ id: "console.create-project.agent", placements: ["console/create-project"], params: { projectId: p.string().example("proj-example") }, template: (t, params) => t` Set up this app on Acme. 1. Install the CLI: ${t.cmd("npm i -g acme@latest")} 2. In the project root: ${t.cmd(init, params)} 3. Run \`npm run build\` and report the output. `, }); ``` ## Render it [#render-it] `render` takes a definition and real values, and returns a plain JSON tree. `toText` turns the tree into the text users copy. ```tsx title="app/projects/[id]/page.tsx" import { render, toText } from "@copybook/core"; import { getStarted } from "@/content/create-project"; export default async function Page({ params }) { const { id } = await params; const prompt = render(getStarted, { projectId: id }); return
{toText(prompt)}
; } ``` The tree marks every param, command, and code block, so a UI can highlight them. The [registry components](/docs/components) render it with copy buttons and tabs. ## Catalog and snapshot [#catalog-and-snapshot] Install the CLI and point it at your definitions: ```npm npm i -D @copybook/cli ``` ```ts title="copybook.config.ts" import { defineConfig } from "@copybook/cli"; export default defineConfig({ content: ["content/**/*.ts"], }); ``` ```bash npx copybook list # what exists, and where it's shown npx copybook snapshot # write rendered snapshots; commit them npx copybook check # in CI: fail when a snapshot is stale ``` ## Next steps [#next-steps] - [Definitions](/docs/concepts/definitions): commands, snippets, prompts, and collections. - [Templates](/docs/concepts/templates): the `t` tag, embedding, and indentation. - [Variants](/docs/concepts/variants): one snippet in many frameworks. - [Snapshots](/docs/concepts/snapshots): reviewable history for every definition. # @copybook/cli (https://copybook.dev/docs/reference/cli) ```npm npm i -D @copybook/cli ``` Every command finds `copybook.config.ts` by walking up from the current directory. Pass `-c, --config ` to use another file. The flag tables on this page come from the CLI itself, and `copybook commands --json` prints the same catalog. > **Version 0.2.0.** `status`, `show`, `commands`, `mcp`, `--json` for every command, and exit code 2 arrived in 0.2.0. ## `init` [#init] Sets up Copybook in the current directory, and is safe to run again. It writes `copybook.config.ts` if there isn't one, installs `@copybook/core` and `@copybook/cli` with the package manager from your lockfile if `package.json` doesn't list them, and sets up coding agents: see [Coding agents](/docs/guides/agents#set-up). | Flag | Description | | --- | --- | | `--no-install` | Skip installing @copybook/core and the CLI. | | `--no-agent` | Skip the AGENTS.md section, skill, and MCP config for coding agents. | | `--dry-run` | Show what would change without writing anything. | | `--json` | Print what changed as JSON. | ## `status` [#status] Shows the config, the definitions, missing, changed, and orphaned snapshots, every issue, the latest test and eval results, and what to do next. It exits 0 once it runs; `clean` in the JSON says whether there's anything to do. Agents start here. | Flag | Description | | --- | --- | | `--json` | Print the status as JSON. | | `-c, --config ` | The config file. | ## `list` [#list] Lists every definition with its kind, variant count, and placements. `--grep` also matches the rendered output, so it finds the definition behind text you see in the UI. | Flag | Description | | --- | --- | | `--json` | Print the manifest as JSON. | | `--grep ` | Only definitions whose id, title, placements, tags, or rendered output contain this text. | | `--changed` | Only definitions whose output changed since --base. | | `--base ` | The git ref to compare against. Defaults to `HEAD`. | | `-c, --config ` | The config file. | ## `show` [#show] Prints one definition as Markdown: its source, placements, the files that render it, embeds and embedders, params, the output of every variant, test and eval results, snapshot history, and next steps. Old ids resolve through aliases, and unknown ids fail with the closest matches. ```bash copybook show acme.cli.init copybook show acme.cli.init --text --values '{"projectId":"proj-123"}' ``` | Flag | Description | | --- | --- | | `--values ` | Param values as JSON, over the examples. | | `--variant ` | Only this snippet variant. | | `--text` | Print only the rendered text, for piping. | | `--json` | Print the details as JSON. | | `-c, --config ` | The config file. | ## `snapshot` [#snapshot] Writes a rendered snapshot of every definition. It only touches files whose content changed. | Flag | Description | | --- | --- | | `--prune` | Delete snapshots of definitions that no longer exist. | | `--json` | Print what was written as JSON. | | `-c, --config ` | The config file. | ## `check` [#check] Validates definitions and compares snapshots. It exits with 1 on any error. See [Snapshots](/docs/concepts/snapshots#check-in-ci). | Flag | Description | | --- | --- | | `--base ` | Also warn about ids removed since this git ref. | | `--json` | Print the issues, snapshot diffs, and next steps as JSON. | | `-c, --config ` | The config file. | ## `test` [#test] Parses, typechecks, and runs commands and snippets. See [Testing](/docs/guides/testing). | Flag | Description | | --- | --- | | `-t, --tier ` | Comma-separated tiers: syntax, typecheck, run. Defaults to `syntax,typecheck`. | | `--only ` | Only these definition ids. | | `--changed` | Only definitions whose output changed since --base. | | `--base ` | The git ref to compare against. Defaults to `HEAD`. | | `--reporter ` | How to print results. One of `pretty`, `json`, `junit`. Defaults to `pretty`. | | `--json` | Print the report as JSON, like --reporter json. | | `-o, --output ` | Also write the json or junit report to a file. | | `--concurrency ` | How many cases run at once. | | `-c, --config ` | The config file. | ## `eval` [#eval] Gives prompts to coding agents and checks what they did. It refuses to run outside CI unless you pass `--unsafe-local`. See [Evals](/docs/guides/evals). | Flag | Description | | --- | --- | | `--only ` | Only these prompt ids. | | `--agent ` | Only these agents. | | `--trials ` | Override each eval's trial count. | | `--budget-usd ` | Stop starting trials after spending this much. | | `--changed` | Only prompts whose output changed since --base. | | `--base ` | The git ref to compare against. Defaults to `HEAD`. | | `--history-dir ` | Append results to /.jsonl, and skip prompts already evaluated. | | `--force` | Re-run prompts that history says are unchanged. | | `--json` | Print the report as JSON. | | `--unsafe-local` | Allow running outside CI. | | `-c, --config ` | The config file. | ## `usages` [#usages] Shows which product files import each definition. Set `usage.include` in the config. Definitions embedded in a rendered one count as used. | Flag | Description | | --- | --- | | `--json` | Print the report as JSON. | | `-c, --config ` | The config file. | ## `report` [#report] Prints a Markdown summary of changed definitions for pull request comments. See [CI](/docs/guides/ci#pull-request-comments). | Flag | Description | | --- | --- | | `--base ` | The git ref to compare against. Defaults to `origin/main`. | | `-c, --config ` | The config file. | ## `studio` [#studio] Starts the local Studio UI and its API. See [Studio](/docs/guides/studio). | Flag | Description | | --- | --- | | `-p, --port ` | The port to listen on. Defaults to `4983`. | | `--host ` | The host to bind. Defaults to `127.0.0.1`. | | `--api-only` | Serve only the JSON API. | | `--json` | Print the URLs as one line of JSON once it's listening. | | `-c, --config ` | The config file. | ## `mcp` [#mcp] Serves the project to coding agents over MCP, on stdio. Your agent's MCP client starts it; see [Coding agents](/docs/guides/agents#mcp). | Flag | Description | | --- | --- | | `-c, --config ` | The config file. | ## `commands` [#commands] Lists every command, with its flags and whether agents can run it without asking. | Flag | Description | | --- | --- | | `--json` | Print the catalog as JSON. | ## JSON output and exit codes [#json-output-and-exit-codes] Every command except `report` takes `--json`. The output is one JSON object on stdout with `schemaVersion: 1`, `ok`, and `next`: the steps to take, each with a `title`, the CLI `argv` and `command`, the MCP `tool`, the affected `ids`, and `safe`, which says whether an agent can take it without asking. When a command fails, `--json` prints an error envelope instead, and the message goes to stderr without it: ```json { "error": { "code": "CONFIG_NOT_FOUND", "message": "No copybook.config.ts found." }, "next": [{ "argv": ["init"], "code": "init", "command": "copybook init" }], "ok": false, "schemaVersion": 1 } ``` | Exit code | Meaning | | --- | --- | | `0` | Everything is fine. | | `1` | The command ran and found problems: errors from `check`, stale snapshots, or failing tests and evals. | | `2` | The command couldn't run: a usage error, no config, content that fails to load, an unknown id or bad values, or `eval` outside CI. | Colors are off when stdout isn't a terminal, with `--json`, and with `NO_COLOR`. `FORCE_COLOR` turns them on. ## Programmatic API [#programmatic-api] The package also exports the building blocks the commands use: `runCli`, `loadAnalysis`, `checkIssues`, `findDefinition`, `searchDefinitions`, `buildStatus`, `definitionDetail`, `buildCatalog`, `loadProject`, `buildSnapshots`, `buildManifest`, `compareSnapshots`, `writeSnapshots`, `changedIds`, and `fileHistory`. ```ts import { buildStatus, loadAnalysis, runCli } from "@copybook/cli"; const status = await buildStatus(await loadAnalysis(process.cwd())); const exitCode = await runCli(["check", "--json"]); ``` # Config (https://copybook.dev/docs/reference/config) ```ts title="copybook.config.ts" import { defineConfig } from "@copybook/cli"; export default defineConfig({ content: ["src/**/*.prompts.ts"], exclude: ["src/legacy/**"], tsconfig: "tsconfig.json", stubs: ["server-only"], verify: ["verify/**/*.verify.ts"], usage: { include: ["src/**/*.tsx"] }, snapshots: { dir: "copybook/snapshots" }, placements: { "console/create-project": { route: "/projects/new" }, }, }); ``` | Option | Description | | --- | --- | | `content` | Globs of files that export definitions, relative to the config file. Required. | | `verify` | Globs of files that export `defineTest`, `defineFixture`, and `defineEval` specs. Your product never imports these. | | `exclude` | Globs to leave out. `node_modules`, `dist`, and `.next` are always excluded. | | `tsconfig` | Used to resolve path aliases such as `@/`. Defaults to `tsconfig.json`. | | `stubs` | Modules to replace with empty ones while loading content, such as `server-only`. CSS imports are always stubbed. | | `snapshots.dir` | Where snapshots are written. Defaults to `copybook/snapshots`. | | `usage.include`, `usage.exclude` | Product files to scan for imports of definitions, for `usages`, `check`, `report`, and Studio. | | `placements` | Known placements, each with an optional `route` and `description`. When you set this, `check` fails on placements that aren't listed. | ## How content loads [#how-content-loads] The CLI bundles your content files with esbuild, so tsconfig path aliases and extensionless imports work, then imports the bundle. npm dependencies stay external and resolve from your `node_modules`. Only **exported** definitions are found, including ones inside exported arrays, objects, and collections. A definition that's only embedded in another one still appears in that definition's snapshot, but it doesn't get a snapshot of its own. Keep content files free of imports that only work inside your app, such as React components or database clients, or list those modules in `stubs`. # @copybook/core (https://copybook.dev/docs/reference/core) ## Definitions [#definitions] | Export | Description | | --- | --- | | `defineCommand(input)` | A shell command. `verify` defaults to `"syntax"`. | | `defineSnippet(input)` | A code block, either single (`lang` + `template` or `code`) or with `dimensions` and `variants`. | | `definePrompt(input)` | Text for a coding agent. | | `defineCollection({ idPrefix, items, key, build })` | Definitions generated from data, with ids `.`. | | `skip(reason)` | A `verify` value that never runs the definition. | | `isDefinition(value)`, `isCollection(value)`, `isFragment(value)` | Type guards based on the brands. A fragment is what a `t` template returns. | | `variantKey(dimensions, when)` | Builds a canonical variant key. | Definitions are frozen objects branded with `DEFINITION` (`Symbol.for("copybook.def")`). Collections are branded with `COLLECTION`, and fragments with `FRAGMENT`. ## Params [#params] `p.string()`, `p.url()`, `p.number()`, `p.boolean()`, `p.enum(values)`, `p.secret({ placeholder, env? })`, and `p.schema(standardSchema)`. Each returns a `Param` with `.example()`, `.default()`, `.optional()`, `.describe()`, and `.toJSON()`. `InputValues` is the type `render` accepts. `TemplateValues` is the type a template receives. ## Rendering [#rendering] | Export | Description | | --- | --- | | `render(definition, values?, options?)` | Renders with real values. Throws if a required param is missing. | | `renderExample(definition, { values?, variant? })` | Renders with example values, filling in any `values` you pass. | | `selectVariant(snippet, selection?)` | Returns the matching variant, or throws. | | `listVariants(snippet)` | Every variant as `{ key, label, when, lang? }`, in declaration order. `label` joins the option labels, such as `AI SDK · .env`. | | `toText(rendered, { format? })` | The text to copy. `format` is `"markdown"` (default) or `"plain"`. | | `toSegments(rendered, { format? })` | Flat segments for highlighted UIs. | | `collectCommands(rendered)` | Every embedded command, as `{ id?, text }`. | | `collectEmbeds(rendered)` | The ids of every embedded definition. | ## The rendered tree [#the-rendered-tree] ```ts interface Rendered { id: string; kind: "command" | "prompt" | "snippet"; lang?: string; // snippets variant?: string; // snippets with variants nodes: Node[]; } type Node = | { type: "text"; value: string } | { type: "param"; name: string; value: string; secret?: true; placeholder?: true; } | { type: "command"; id?: string; children: Node[] } | { type: "code"; id?: string; lang?: string; variant?: string; children: Node[]; } | { type: "include"; id: string; kind: DefinitionKind; children: Node[] }; ``` `Rendered` is plain JSON. Render on the server and pass it to client components.