AGENTS.md@apps/docs · git:20261012.5769a7b · 2026-10-12 · sha256 86aa5e41f5348c5d

AGENTS.md@apps/docs git:20261012.5769a7bA

Immutable. This exact content never changes and is served at /api/v1/blob/86aa5e41f5348c5d.

# Agent Instructions — `apps/docs`

The documentation site. Read together with the [root `AGENTS.md`](../../AGENTS.md)
and [`apps/AGENTS.md`](../AGENTS.md).

Fumadocs on Next.js, laid out as Fumadocs' Next.js template
(`create-fumadocs-app`) lays it out: `app/docs` serves `content/docs`,
`lib/source.ts` defines the collection with Fumadocs MDX's macro and loads it,
and the search, `llms.txt`, Markdown, Open Graph and MCP routes are Fumadocs'
own recipes. The look is Fumadocs' Spacious layout, the one fumadocs.dev uses,
without a theme of our own. Reach for a Fumadocs recipe before writing
something of our own.

## Everything under `/docs`

The hosted cloud routes only `/docs` and `/docs/(.*)` to this app; every other
path belongs to the server (repo-root [`vercel.json`](../../vercel.json),
[`apps/cloud/README.md`](../cloud/README.md)). So the template's root routes
live under `/docs` here:

| Route | Template |
| --- | --- |
| `/docs/api/search` | `/api/search` |
| `/docs/api/mcp` | `/api/mcp` (`fumadocs-core/mcp`) |
| `/docs/llms.txt`, `/docs/llms-full.txt` | `/llms.txt`, `/llms-full.txt` |
| `/docs/og/<page>/image.png` | `/og/docs/<page>/image.png` |
| `/docs/<page>.md`, the overview `/docs/index.md` | the same, through `proxy.ts` |

- A new route goes under `app/docs/`, never at the app's root. `app/llms.mdx/`
  is the one exception: only `proxy.ts` reaches it.
- A route under `app/docs/` that is not a page goes into the `matcher` of
  `proxy.ts` as an exclusion, or an agent asking for Markdown gets a 404.
- Scripts and styles load from `/docs/_next/` (`assetPrefix`). Do not switch to
  `basePath`: the URLs in `llms.txt`, the Markdown and the MCP answers would
  lose their `/docs`.
- A link in the content is the page's full path: `/docs/cli`, not `/cli`.

## Content

- A page's URL is its path under `content/docs/`. The API reference's own pages
  are in `content/docs/reference/api/`; a page per operation comes from the
  OpenAPI document (`lib/source.ts`).
- `moon run docs:generate` bundles `api/openapi` into `.generated/openapi.mjs`,
  which `lib/openapi.ts` imports. The content needs no generation step.
- A page's Markdown (`/docs/cli.md`, or `/docs/cli` with `Accept:
  text/markdown`) is its processed Markdown; an operation's is the operation
  with every schema it references (`docsLlms` in `lib/source.ts`).
  `llms-full.txt` leaves the operations out.

## Checks

`moon run docs:typecheck docs:lint docs:build`, and `moon run docs:start` to
look at the build on port 3003. On Vercel the build runs `pnpm run build`,
never moon: moon cannot archive the symlinks Vercel's Next.js adapter writes
into `.next/output`.

## Tests

`tests/` is the site's contract suite (Playwright, `playwright.config.mts`),
written against a URL rather than the code. `moon run docs:e2e` runs it
against the local build on port 3003; `DOCS_BASE_URL=https://cloud-….vercel.app
moon run docs:e2e-deployment` against a deployment of the hosted cloud, with
`VERCEL_AUTOMATION_BYPASS_SECRET` for its protection, which
[`deployment-checks.yml`](../../.github/workflows/deployment-checks.yml) does for every
deployment. The specs use paths only, so one spec proves both.

- `/docs/llms.txt` is the suite's page list: every page it names is fetched as
  HTML, at its `.md` URL and through `Accept: text/markdown`, and every site
  link in the Markdown must resolve. A page needs no test of its own.
- The routes that are not pages each have a test in `tests/agents.spec.ts`:
  search, the MCP server (`initialize`, `tools/list`, `get_page`), the
  plain-text answers, the Open Graph image. A new route under `app/docs/` gets
  one there, and goes into the matcher of `proxy.ts` (above).
- `tests/pages.spec.ts` also pins the assets to `/docs/_next/` and
  `llms-full.txt` to the pages without the API operations.
- One browser test (`*.browser.spec.ts`, Chromium) proves the site hydrates
  and navigates; everything else is HTTP through Playwright's request fixture.