5.6 KiB
5.6 KiB
AGENTS.md - AI Coding Agent Guidelines
Project Overview
TanStack Start fullstack app with Tauri desktop shell.
| Layer | Tech |
|---|---|
| Framework | TanStack Start (React SSR, file-based routing) |
| Runtime | Bun |
| Language | TypeScript (strict mode) |
| Styling | Tailwind CSS v4 |
| Database | PostgreSQL + Drizzle ORM |
| RPC | ORPC (contract-first, type-safe) |
| Build | Vite + Turbo |
| Linting | Biome |
| Desktop | Tauri v2 (optional, see src-tauri/AGENTS.md) |
Commands
# Development
bun dev # Start Tauri + Vite via Turbo
bun dev:vite # Vite only (localhost:3000)
bun db:studio # Drizzle Studio
# Build
bun build # Full build (Vite → compile → Tauri)
bun build:vite # Vite only (outputs to .output/)
# Code Quality
bun typecheck # TypeScript check (tsc -b)
bun fix # Biome auto-fix (format + lint)
# Database
bun db:generate # Generate migrations from schema
bun db:migrate # Run migrations
bun db:push # Push schema changes (dev only)
# Testing (not configured yet)
# When adding tests, use Vitest or Bun test runner:
# bun test path/to/test.ts # Single file
# bun test -t "pattern" # By test name
Code Style
Formatting (Biome enforced)
- Indent: 2 spaces
- Line endings: LF
- Quotes: Single
'string' - Semicolons: As needed (ASI)
- Arrow parens: Always
(x) => x
Imports
Biome auto-organizes. Order: external → internal (@/*) → type-only imports.
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
import { db } from '@/db'
import type { ReactNode } from 'react'
TypeScript
Strict mode with extra checks:
noUncheckedIndexedAccess: true- array/object index returnsT | undefinednoImplicitOverride: trueverbatimModuleSyntax: true
Path alias: @/* → src/*
Naming
| Entity | Convention | Example |
|---|---|---|
| Files (utils) | kebab-case | utils.ts, db-provider.ts |
| Files (components) | PascalCase | NotFound.tsx |
| Routes | TanStack conventions | routes/index.tsx, routes/__root.tsx |
| Components | PascalCase arrow functions | const MyComponent = () => {} |
| Functions | camelCase | handleSubmit |
| Constants | UPPER_SNAKE_CASE | MAX_RETRIES |
| Types/Interfaces | PascalCase | TodoItem, RouterContext |
React Patterns
// Components: arrow functions (Biome enforces)
const MyComponent = ({ title }: { title: string }) => {
return <div>{title}</div>
}
// Routes: createFileRoute
export const Route = createFileRoute('/')({
component: Home,
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(orpc.todo.list.queryOptions())
},
})
// Data fetching: TanStack Query
const query = useSuspenseQuery(orpc.todo.list.queryOptions())
const mutation = useMutation(orpc.todo.create.mutationOptions())
ORPC Pattern (Contract-First RPC)
- Define contract (
src/orpc/contracts/my-feature.ts):
import { oc } from '@orpc/contract'
import { z } from 'zod'
export const get = oc.input(z.object({ id: z.uuid() })).output(schema)
export const create = oc.input(insertSchema).output(schema)
- Implement handler (
src/orpc/handlers/my-feature.ts):
import { os } from '@/orpc/server'
import { dbProvider } from '@/orpc/middlewares'
export const get = os.myFeature.get
.use(dbProvider)
.handler(async ({ context, input }) => {
return await context.db.query.myTable.findFirst(...)
})
- Register in
contract.tsandrouter.ts - Use in components via
orpc.myFeature.get.queryOptions()
Drizzle Schema
import { sql } from 'drizzle-orm'
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
export const myTable = pgTable('my_table', {
id: uuid('id').primaryKey().default(sql`uuidv7()`),
name: text('name').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow().$onUpdateFn(() => new Date()),
})
Environment Variables
- Server: no prefix (e.g.,
DATABASE_URL) - Client:
VITE_prefix required - Validated via
@t3-oss/env-coreinsrc/env.ts
Directory Structure
src/
├── components/ # Reusable React components
├── db/
│ ├── schema/ # Drizzle schema definitions
│ └── index.ts # Database instance
├── integrations/ # TanStack Query/Router setup
├── lib/ # Utility functions
├── orpc/
│ ├── contracts/ # Input/output schemas
│ ├── handlers/ # Server procedure implementations
│ ├── middlewares/ # Middleware (e.g., dbProvider)
│ ├── contract.ts # Contract aggregation
│ ├── router.ts # Router composition
│ └── client.ts # Isomorphic client
├── routes/ # File-based routes
│ ├── __root.tsx # Root layout
│ └── api/rpc.$.ts # ORPC HTTP endpoint
└── env.ts # Environment validation
Critical Rules
- DO NOT edit
src/routeTree.gen.ts(auto-generated) - DO NOT commit
.envfiles - MUST run
bun fixbefore commits - MUST use
@/*path alias (not relative imports) - MUST use React Compiler (no manual memoization needed)
- MUST use
Readonly<T>for immutable props
Git Workflow
- Make changes following style guide
- Run
bun fix(format + lint) - Run
bun typecheck(type safety) - Test with
bun dev - Commit with descriptive message