AGENTS.md
Project configuration and coding guidelines for AI agents, covering i18n, service patterns, Effect schemas, HTTP API groups, optimistic atoms, and lint/typecheck commands.
bunx --bun shadcn@latest add https://krakstack.net/r/agents.jsonOverview
agents installs the AGENTS.md file that guides AI agents working in a KrakStack project. It covers i18n conventions, code architecture, Effect service patterns, schema and API documentation rules, and the commands used to validate changes.
Sections
- i18n — Paraglide.js conventions, message file ownership, and English/French requirements
- Code Architecture — Frontend (TanStack Start, shadcn, Effect atoms) and backend (Effect, Drizzle, PostgreSQL) structure
- Patterns — Server services, frontend client components, database schema, API composition, schema annotations, and documentation rules
- Examples — Effect schema, API group, API builder, form dialog, data table, optimistic atom, Effect service, and API entry point code
- Preferences — Arrow functions, no type assertions
- Checks —
bun type:check,bun lint,bun fmt
Usage
Place AGENTS.md at the project root so agent tooling can read it automatically:
npx shadcn@latest add https://krakstack.net/r/agents.jsonAfter installation, open AGENTS.md and adjust the folder structure, patterns, and check commands to match your project.
Preview
AGENTS
Core Rules
- Prefer the smallest correct change that fits the existing architecture.
- Keep frontend and backend concerns separated unless a feature explicitly spans both.
- Use project conventions already present in nearby files before introducing new patterns.
- Do not edit generated or registry-managed files unless explicitly requested.
i18n
- English and French are required for all public-facing strings.
- Translate public-facing strings with paraglide.js and the Vite plugin.
- Store translations in
src/messages/en.jsonandsrc/messages/fr.json. - Import generated messages from
src/paraglide/messagesinstead of hardcoding UI copy.
Architecture
The application is divided into two areas: frontend and backend.
Frontend
- React with TanStack Start and TanStack Router.
- shadcn UI primitives and installed registry components.
- Effect service state for data loading and mutations.
- Prefer existing app form, table, dialog, and data-fetching patterns before adding new abstractions.
Backend
- Effect application services.
- Effect Postgres and Drizzle ORM for database access.
- Effect HttpApi, HttpServer, OpenAPI, and OpenTelemetry for API and runtime concerns.
Folder Structure
public/contains static assets.scripts/contains build and utility scripts.tmp/contains local temporary files that should not be committed.src/components/contains React components.src/components/ui/contains shadcn-managed primitives. Do not edit directly.src/db/contains Drizzle schema definitions.src/hooks/contains shared React hooks.src/lib/contains shared utilities and auth config.src/messages/contains i18n source files.src/paraglide/contains generated i18n runtime. Do not edit directly.src/routes/contains TanStack Start file-based routes.src/routes/api/contains the API catch-all route.src/routes/docs/contains documentation pages.src/services/contains Effect service definitions, API handlers, schemas, and client state.src/api.tsdefines the root Effect API.
Code Practices
- Prefer arrow functions
() => voidover function expressionsfunction () {}except where Effect generator APIs requirefunction*. - Avoid
as any,as Type, andas unknownunless absolutely necessary. - Use Effect
Schemafor validation. Do not use Zod or other validation libraries. - Prefer Effect-native integrations over ad hoc boundaries: use
FetchHttpClient/HttpClientinstead of rawfetch, EffectSchemacodecs such asSchema.fromJsonString(...)andHttpClientResponse.schemaBodyJson(...)instead of manualJSON.parseor custom validation, and typed Effect errors instead of broadtry/tryPromisewrappers. - Use
Effect.tryorEffect.tryPromiseonly when wrapping a non-Effect API that has no suitable Effect adapter; keep the boundary as small as possible and map failures into domain-specific errors. - Annotate schemas with
.annotate({ identifier: "Name" }). - Use
Schema.toStandardSchemaV1(...)when integrating Effect schemas with form validators. - Use
Effect.fnfor service methods when practical. - Add OpenTelemetry through Effect runtime patterns where relevant.
Schema
- Use Effect
Schemafor all parsing, decoding, validation, and type-safe boundary checks. - Define reusable schemas in the nearest
schema.tsfile and annotate them with.annotate({ identifier: "Name" }). - Validate untrusted inputs at boundaries using Effect schema decoders, including API payloads, query params, route params, form inputs, external API responses, environment variables, JSON blobs, and persisted data.
- Do not write custom runtime validation such as
typeof value === "object",Array.isArray(value), manual property checks, custom type guards, or ad hocJSON.parsevalidation when an EffectSchemacan express the shape. - Prefer Effect codecs and helpers such as
Schema.decodeUnknown,Schema.decodeUnknownSync,Schema.fromJsonString(...), andHttpClientResponse.schemaBodyJson(...)over manual parsing. - Keep validation failures typed and explicit. Map schema parse errors into domain-specific errors where needed instead of throwing broad errors.
- Use custom predicates only inside Effect schema refinements or filters, and only when the rule cannot be represented with built-in schema combinators.
Services
Use service-based design for CRUD, features, integrations, and related domain concerns.
A typical service should use this structure:
src/services/<name>/schema.tsdefines Effect schemas, payload schemas, route params, and standard schema exports.src/services/<name>/index.tsimplements the EffectContext.Serviceand exposes production and test layers where needed.src/services/<name>/api.group.tsdefines the HttpApiGroup contract.src/services/<name>/api.builder.tswires the service into the root API with auth and error mapping.src/services/<name>/client/atom.tsdefines query and mutation atoms.src/services/<name>/client/form.tsxdefines reusable create/edit forms.src/services/<name>/client/table.tsxdefines data tables and row actions.
Service methods should accept object inputs, scope by the current user or tenant where applicable, and avoid exposing cross-tenant data.
API
- Define the root API in
src/api.ts. - Merge service API groups into the root API with
.add(...). - Keep OpenAPI annotations on the root API.
- OpenAPI documentation is served at
/api/docs. - MCP server support is served at
/api/mcpand should use@krak-stack/httpapi-mcp. - CLI support should use
@krak-stack/httpapi-cli.
Tooling
- Use KrakStack Auth for user management, auth components, sessions, and organizations.
- Use KrakStack Components where possible and keep installed registry components current.
- Install KrakStack registry items with shadcn using the
@krak-stackregistry alias configured incomponents.json; do not copy registry item files manually unless explicitly requested. - Before creating a custom component, check the shadcn MCP server for a compatible component or registry item.
- Use shadcn through the registry workflow. If needed, initialize MCP with
bunx --bun shadcn@latest mcp init --client opencode.
Testing
- Use Vitest with
@effect/vitest. - Add tests beside code when practical using
*.test.tsor*.test.tsx. - Import
describe,expect, anditfrom@effect/vitest. - Use
it.effectfor Effect programs and provide dependencies withEffect.provide(...). - Prefer fresh per-test layers so mutable state does not leak.
- Use suite-shared layers only for expensive resources and reset state between tests.
- Backend and service tests must use the real Postgres test database through
TEST_DATABASE_URL. - Never point tests at
DATABASE_URL. - The test database is provided externally. Set
TEST_DATABASE_URLin.envor the shell before DB tests. - Expose service
testLayers for tests, backed byDB.testLayerwhere database access is needed. - Run migrations against the test database before DB tests and reset affected tables between tests.
- Use Drizzle queries for test setup and cleanup where possible.
- Avoid raw SQL unless a migration or lifecycle task requires it.
End-to-End Testing
- Use Playwright for browser-level tests of user journeys, UI behavior, routing, authentication, and frontend-to-API integrations.
- Keep end-to-end tests in
e2e/as*.spec.tsfiles and share repeated setup through focused helpers. - Run
bun run test:e2e:installonce when Chromium is not installed,bun run test:e2efor the full suite, andbun run test:e2e:uiwhen interactive debugging is useful. - Use the Playwright CLI to exercise changed UI and integrations as you build, not only after implementation is complete. Start with the smallest relevant spec or title filter, inspect the browser result, and rerun after each meaningful change before running the full suite.
- Run a focused test with
bun run test:e2e -- e2e/<name>.spec.tsorbun run test:e2e -- --grep "<test name>". - Prefer assertions against user-visible outcomes and accessible locators such as
getByRole,getByLabel, andgetByText. Avoid implementation-coupled selectors and arbitrary sleeps. - Cover complete high-value flows across UI and API boundaries. Keep lower-level edge cases in Vitest rather than duplicating them in browser tests.
- Make test data unique and deterministic, isolate browser contexts where roles or sessions differ, and clean up persistent state when a test can affect later runs.
- End-to-end tests must use
TEST_DATABASE_URL; never useDATABASE_URL. The Playwright configuration maps the test database into the application process and starts the development server automatically. - Use Playwright traces, screenshots, and the UI runner to diagnose failures. Do not weaken assertions, add unconditional delays, or increase timeouts until the underlying behavior has been investigated.
- Before considering a UI or integration change complete, run the focused Playwright coverage for the changed journey and, when practical, the full end-to-end suite.
Checks
Run checks after code changes when practical:
bun run testbun run test:e2ebun type:checkbun lintbun fmt
Examples
KrakStack examples are the canonical architecture reference for this project. Prefer the configured krakstack project reference. If it is unavailable, use https://github.com/krakcons/krakstack/tree/main/src/agent-examples.
Before implementing or substantially refactoring one of the areas below, read the corresponding KrakStack example and the nearest equivalent implementation in this repository.
| Task | Required KrakStack reference | | --------------------- | ------------------------------------------- | | Effect service | src/agent-examples/service/service.ts | | Effect schemas | src/agent-examples/service/schema.ts | | HttpApi contract | src/agent-examples/service/api.group.ts | | HttpApi handlers | src/agent-examples/service/api.builder.ts | | Root API registration | src/agent-examples/service/api-entry.ts | | Client atoms | src/agent-examples/service/atom.ts | | Forms | src/agent-examples/service/form.tsx | | Tables | src/agent-examples/service/table.tsx |
<!-- intent-skills:start -->
Skill Loading
Before substantial work:
- Skill check: run
npx @tanstack/intent@latest list, or use skills already listed in context. - Skill guidance: if one local skill clearly matches the task, run
npx @tanstack/intent@latest load <package>#<skill>and follow the returnedSKILL.md. - Monorepos: when working across packages, run the skill check from the workspace root and prefer the local skill for the package being changed.
- Multiple matches: prefer the most specific local skill for the package or concern you are changing; load additional skills only when the task spans multiple packages or concerns.
<!-- intent-skills:end -->