Imported from lemonmade/quilt (
AGENTS.md). Install upstream withnpx skills add lemonmade/quilt. Copyright stays with the author.
Quilt — Agent & Contributor Guide
Quilt is a TypeScript-first framework for building web apps, shared packages, and backend services. It is built on Preact (with a React compatibility layer), uses Vite for local development and Rollup for production builds, and ships roughly 20 KB (minified + compressed) for a full-featured app including Preact itself.
Repository layout
quilt/
├── packages/ # ~40 framework packages (all @quilted/* scoped)
├── integrations/ # 5 platform integrations (Cloudflare, Deno, HTMX, React Query, tRPC)
├── documentation/ # Markdown docs for the framework
├── tests/ # Unit and E2E test suites
├── configuration/ # Shared Vite/Vitest config
└── .github/workflows/ # CI/CD pipelines
All packages and integrations are managed as a pnpm workspace (see pnpm-workspace.yaml). The root package.json is private and never published.
Key commands
All commands are run from the repository root with pnpm.
| Command | What it does |
|---|---|
pnpm test |
Run unit tests with Vitest (watch mode in terminal) |
pnpm test.e2e |
Run E2E tests with Vitest + Playwright |
pnpm type-check |
TypeScript build check across all packages (tsc --build) |
pnpm lint |
Check formatting with Prettier |
pnpm format |
Auto-fix formatting with Prettier |
pnpm build |
Production build (Rollup first, then all other packages) |
pnpm changeset |
Create a changeset entry for your changes (required before merging) |
pnpm version-bump |
Bump versions from pending changesets |
Build order matters. The
buildscript intentionally builds@quilted/rollupbefore everything else, because other packages depend on it:pnpm --filter rollup run build && pnpm --filter !rollup run build.
CI runs lint → type-check → unit tests → E2E tests in sequence. All four must pass before merging.
Package conventions
Source vs. build imports
Packages expose a quilt:source export condition pointing at raw TypeScript source (e.g. ./source/index.ts). This is used during development and by tsx scripts so you never need to pre-build packages to work on them locally.
"exports": {
".": {
"quilt:source": "./source/index.ts",
"quilt:esnext": "./build/esnext/index.js",
"import": "./build/esm/index.js"
}
}
When writing scripts or tests that import workspace packages, pass --conditions quilt:source (or use the typescript.run / typescript.watch root scripts):
pnpm typescript.run ./some-script.ts
TypeScript project references
The root tsconfig.json uses TypeScript project references. When adding a new package or a new cross-package dependency, update both the consuming package's tsconfig.json references array and the root tsconfig.json.
React compatibility
Quilt uses Preact, not React. The packages @quilted/react and @quilted/react-dom re-export Preact under the React namespace so that most third-party React packages work without modification. Prefer importing from @quilted/quilt (or the specific @quilted/preact-* package) rather than react in framework code.
Project types
Quilt recognizes three kinds of projects inside a workspace:
- App — a browser application with optional server-side rendering. Vite handles local dev; Rollup handles production. Multiple browser targets are built automatically.
- Package — a shareable library, built with Rollup into ESM (and optionally CJS). Quilt uses itself to build itself.
- Service — a backend HTTP server, typically using Hono. Can be deployed to Node, Cloudflare Workers, Deno, etc.
Templates for all three are available via pnpm create @quilted <app|package|service>.
Making changes
- Edit source in
packages/<name>/source/orintegrations/<name>/source/. - Run checks —
pnpm type-check && pnpm test && pnpm lint. - Create a changeset —
pnpm changeset. Select the affected packages and describe the change. Edit the generated.changeset/*.mdfile to provide context. - Open a PR. The changeset bot will create a version-bump PR automatically once your changes land on
main.
Changesets are required for any change that affects a published package. Omit them only for docs-only or internal tooling changes.
When creating a new package, use pnpm create @quilted package to scaffold the package, which uses the public version of the @quilted/create package to create the package (so meta!).
Design priorities (abbreviated)
- Performance — ship only what you use; tree-shake aggressively; multi-browser-target builds.
- Type safety — TypeScript-first, including type-safe GraphQL via Quilt's own codegen.
- Component-first — features (routing, HTML, localization) are enabled by rendering components, not config.
- Explicitness — opt in to features rather than having them active by default.
- Small-but-mighty — curated, replaceable tools over framework bloat.
Full design rationale: documentation/priorities.md.
Testing
- Unit tests live alongside source in
packages/<name>/source/or intests/. Run withpnpm test. - E2E tests live in
tests/and use Playwright. Run withpnpm test.e2e. - Test utilities:
@quilted/preact-testing/@quilted/react-testingwrap Preact's test renderer with helpful utilities. - Vitest config:
vitest.workspace.js/configuration/vite.unit.config.ts/packages/*/vite.config.ts(unit) andconfiguration/vite.e2e.config.ts(E2E).
E2E tests
The E2E tests are the main way this project guards against regressions. They create whole projects using the Quilt framework, then run build and other commands, visit the app in a headless browser, and make assertions on the rendered page.
Each test starts the same way: creating a temporary "workspace" for the project:
it('renders the app', async () => {
await using workspace = await Workspace.create({fixture: 'empty-app'});
const server = await startServer(workspace);
const page = await server.openPage();
expect(await page.textContent('body')).toBe('Hello world');
});
These temporary projects are placed in /tests/e2e/output/*/, using a randomly-generated name, deleted at the end of each test. You can provide a debug: true option to keep the workspace around for debugging, and a name option to name the output directory something specific. It is common to do this with an it.only() call to focus on a single test:
it.only('renders the app', async () => {
await using workspace = await Workspace.create({
fixture: 'empty-app',
debug: true,
});
const server = await startServer(workspace);
const page = await server.openPage();
expect(await page.textContent('body')).toBe('Hello world');
});
You can run individual tests with pnpm test.e2e <path> command. Use the CI=1 environment variable to run the tests with the same configuration as the CI environment.
Key packages reference
| Package | Description |
|---|---|
@quilted/quilt |
Umbrella entry point — re-exports async, browser, context, events, localize, navigation, performance, signals, testing, graphql, server, hono, threads, modules |
@quilted/vite |
Vite plugin for Quilt apps and packages |
@quilted/rollup |
Rollup plugin/config for production builds |
@quilted/preact-router |
File-based and component-based routing |
@quilted/preact-async |
Code-splitting and async component loading |
@quilted/preact-browser |
HTML document management (<Title>, <Meta>, etc.) |
@quilted/graphql |
Type-safe GraphQL client + codegen |
@quilted/threads |
Message-passing between JS environments (workers, iframes) |
@quilted/http |
HTTP utilities and typed request/response helpers |
@quilted/create |
pnpm create @quilted scaffolding CLI |
@quilted/cloudflare (integration) |
Cloudflare Workers deployment adapter |
@quilted/hono (integration) |
Hono integration for Quilt services |
Full package list: packages/ and integrations/.
Documentation
Human-readable guides live in documentation/:
getting-started.md— create your first app, package, or servicepriorities.md— design philosophy and tradeoffstools.md— pnpm, Vite, Rollup, Vitest, Prettier, tsxfeatures/— routing, async, GraphQL, HTML, HTTP, localization, server rendering, workers, styles, etc.projects/— app, package, and service deep-divesintegrations/— Hono, HTMX, Tailwind, tRPC, Web Vitalstechnology/— Preact, TypeScript, GraphQL background