AGENTS.md · git:20261030.19f5e83 · 2026-10-30 · sha256 884ef73d2d396647
AGENTS.md git:20261030.19f5e83A
Immutable. This exact content never changes and is served at /api/v1/blob/884ef73d2d396647.
<!-- BEGIN:turborepo-agent-rules -->
# This is NOT the Turborepo you know
Turborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p "require.resolve('turbo/package.json')"` from a workspace that depends on `turbo`.
Read `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.
This block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `"agentGuidance": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.
<!-- END:turborepo-agent-rules -->
# Chai UI: notes for AI coding agents
Chai UI is the front-end surface of AI apps: components for prompting,
analyzing media, showing results and editing images. It's published to npm
as five packages that share one version:
- `packages/tokens` (`@chai-ui/tokens`): design tokens. `src/tokens.json` is
the only source; `build.mjs` emits CSS variables (with the opt-in dark
theme from `color.dark`), JS/TS and a Figma set.
- `packages/core` (`@chai-ui/core`): everything framework-free. The data
model (`run.ts`), the stores and the components' derived state
(`composer.ts`, `composer-view.ts`, `edit-card.ts`, `media-analyzer.ts`),
engines (`engines/fal.ts`, `engines/openrouter.ts`, `engines/vercel.ts`),
model routing, suggested models, prompts (media analysis, the evaluation
judge), and the server route (`src/server`, exported as
`@chai-ui/core/server`, server-only).
- `packages/react` (`@chai-ui/react`, React 18.3 and 19) and
`packages/vue` (`@chai-ui/vue`, Vue 3.5): thin bindings with the same
components (`Composer`, `MediaAnalyzer`, `ResultCard`, `EditCard`),
`ChaiProvider`, `useComposer`, `useMediaAnalyzer` and the primitives.
- `packages/svelte` (`@chai-ui/svelte`, Svelte 5.20 and later): the same
components with React's prop and hook names (only `class` and a string
`style` differ). Built with `svelte-package`, which ships the `.svelte`
files for the app's own compiler. The hooks use runes
(`useComposer.svelte.ts`) and return objects with getters, so they're read
as `composer.run`. Components render on the server (SvelteKit), so
browser-only APIs belong in effects or event handlers, never in
`onDestroy` or the script body; `server-render.ssr.test.ts` checks it.
- `packages/shared` (`@chai-ui/shared`, private, never published): the one
stylesheet, `DESIGN.md`, and the contract tests every binding builds and
tests from, so no binding is another's source.
## Commands
```sh
pnpm install
pnpm build # turbo: tokens -> core -> react, vue and svelte
pnpm lint # token rules + the rules of React (eslint-plugin-react-hooks)
pnpm typecheck
pnpm test # unit tests, then real-browser component + axe tests, for every binding
pnpm pack-test # packs, installs into the sandbox apps (React, Vue, SvelteKit) with npm, builds them
pnpm check-docs # after build: docs name real paths and exports (scripts/check-docs.mjs)
```
When you add a source file, an export or a feature, update the docs that
describe it in the same change (`pnpm check-docs` catches the mechanical
part; reread the wording yourself).
Browser tests need Chromium once:
`pnpm --filter @chai-ui/react exec playwright install chromium`.
## How the bindings stay identical
- **Behavior goes in core first.** A component's logic and derived state
belong in `@chai-ui/core`; React, Vue and Svelte only render it and wire
events.
- **One test suite for all.** Component behavior is tested once, as
contracts in `packages/shared/contracts/`, which every
binding runs. A change to behavior needs a contract test.
- **One stylesheet and one `DESIGN.md`.** Both live in `packages/shared`
(private, never published) with the contracts, and every binding copies
them at build. Edit only those; the bindings' copies are ignored by git.
## Rules
- **Front end only.** Chai covers what the person sees and does. Model
calls, latency, cost and storage belong to the builder's back end.
- **Colors come only from tokens.** No color literals in component CSS; a
new color is a new token in `tokens.json`. `pnpm lint` enforces it.
- **Components are controlled.** Values in as props, changes out as
callbacks. A callback's presence is the opt-in for its control (e.g. no
`onEnhance`, no optimize button).
- **Follow the rules of React.** The React Compiler lint rules are on and
must stay clean: no reading or writing refs during render, no setState in
effects for derived state (use `useLatest` for latest-value refs).
- **Accessibility: support WCAG 2.2 AA.** Every control has an accessible
name, works by keyboard, and has a 24×24px target. Add new states to the
component's "passes axe" contract test (`wcagViolations`, in
`packages/shared/contracts/axe.ts`); axe must stay at zero findings.
Don't claim compliance in docs: Chai "supports" WCAG 2.2 AA, tested with
automated checks.
- **No real model calls in tests.** Inject a fake engine or `fetchImpl`.
Call `fetch` through a wrapper, never as `obj.fetchImpl(...)`: browsers
throw "Illegal invocation".
- **API keys never reach the browser.** Engines call `/api/chai/*`, served
by `createChaiHandler`, which reads `FAL_KEY` / `OPENROUTER_API_KEY` /
`AI_GATEWAY_API_KEY`.
- **Defaults are suggestions.** Ship working defaults (e.g. the reasoning
models in `ChaiProvider`, the suggested model lists) and document how to
swap them; never require a builder to pick before anything works.
- **Opt-in features that call a model have no default model.** Voice input
and the evaluation judge run only once the builder names the model.
- **Plain language in docs and messages.** Builders range from beginner to
intermediate.
- **Changesets.** A change to a published package needs `pnpm changeset`:
`patch` for fixes, `minor` for new options or features. Merging the
"Version Packages" PR publishes to npm (trusted publishing,
`release.yml`).
- **This repo is public.** Keep comments self-contained: no references to
private documents, internal decisions or people.
## Related
The docs site (chai-ui.com/docs) lives in a separate repository and uses
these packages as a consumer. It must never be the place a package fix
lands.