AGENTS.md · git:20260908.d7a9868 · 2026-09-08 · sha256 202e610c67781a64
AGENTS.md git:20260908.d7a9868A
Immutable. This exact content never changes and is served at /api/v1/blob/202e610c67781a64.
# x-md - Landing: `index.html` (`src/main.ts`), shared chrome in `src/chrome.ts`. Docs: Blume MDX in `docs/`, configured by `blume.config.ts`, mounted at `/docs`. Build Blume before Vite; Vite preserves the generated docs in `dist`. - Tokens are the Tailwind v4 `@theme` block in `src/tokens.css` (shared by the landing page and Blume) (`ink`, `ink-2`, `ink-3`, `ink-4`, `surface`, `raised`, `raised-2`, `line`, `hair`, `accent`, `accent-deep`, `accent-soft`, `cyan`, dark `code`/`code-ink`/`code-dim`). Use them, not hex. - Look: warm paper `#f7f6f2`, Satoshi, deep green `#146c43`, pill buttons, 16px cards, near-black code panes. Not pcstyle.dev styling. - Landing copy frame (Theo's feedback): "Tweets are just markdown now", what-you-see vs what-your-agent-sees curl comparison in the `#how` bento, skill install under `#agents`. No stack or provider talk on the landing page. - Motion: GSAP + ScrollTrigger in `setupMotion` (`src/main.ts`), all gated by `prefers-reduced-motion` via `gsap.matchMedia`. - Status URLs serve Markdown by default. Discord, Telegram, and Slack preview bots get Open Graph HTML from `lib/embed.ts` via `api/convert.ts`; `GET /oembed` rewrites to `api/oembed.ts`. - Landing content is pre-rendered at build time: `src/landing.ts` holds the markup, `src/vite-prerender-plugin.ts` writes it into `index.html`, and `src/main.ts` only enhances what is already there. The page must stay readable with JavaScript off, so put content in `landing.ts`, not in a runtime DOM build. - `middleware.ts` (Vercel Routing Middleware) does Accept-based content negotiation for `/`, `/docs/*`, `/about`, `/contact`, `/privacy`, `/terms`: `Accept: text/markdown` or `?mode=agent` rewrites to the page's `.md` sibling, an unsatisfiable Accept is `406`, and every response gets `Vary: Accept` plus RFC 8288 discovery `Link`s. The parser is `lib/negotiate.ts`. A new negotiated page needs both a `.md` sibling and a matcher entry. - Every API failure goes through `lib/apierror.ts`: RFC 9457 problem details built from `ERROR_CATALOG`, which is the single source of the machine `code`, its status, title, and `resolution`. Add a code there, not inline — `lib/openapi.ts`, `api/index.ts`, and `docs/errors.mdx` all render from it. - Routes are versioned: `/api/v1/*` is the stable machine surface, the permalink routes are the unversioned product surface, and `/api/convert` and `/api/browse` are deprecated aliases that must keep emitting `Deprecation`/`Sunset`/`successor-version` (`setDeprecationHeaders` in `lib/apierror.ts`). The published policy is `docs/versioning.mdx`; keep it, `x-api-lifecycle` in `lib/openapi.ts`, and the `versioning` block in `api/index.ts` in agreement. - Quotas live in `lib/quotas.ts` and are advertised by `lib/ratelimit-headers.ts`. Policy names ship in response headers and the OpenAPI document, so renaming one is a breaking change. - `lib/openapi.ts` is the OpenAPI 3.1 source; `bun scripts/build-openapi.ts` renders the committed `public/openapi.json` and `--check` fails when it is stale. Discovery documents are hand-written under `public/` and `public/.well-known/` (`llms.txt`, `agents.md`, `auth.md`, `pricing.md`, `ard.json`, `ai-catalog.json`, `api-catalog`, `mcp/server-card.json`); Vite copies `public/` last, which is how `openapi.json` overrides blume's own. - Deploy: Vercel after `bun run build`. `vercel.json` configures `dist`, the API handlers, and the public route rewrites. - Live search provider: `lib/xsearch.ts`; public allowance is a fixed 10 per IP per 15 min plus a shared pool computed in `lib/pool.ts`. Counters live in `lib/ratelimit.ts` (shared Redis via `lib/redis.ts`, memory fallback). Keep provider internals and capacity math out of public prose; describe it as a custom-built provider. - Checks before pushing: `bun --bun tsc`, `bun run test`, `bun run build`. `main` is protected: open a PR, CI `build` and CodeRabbit must pass.