# AGENTS.md - AI Coding Agent Guidelines Guidelines for AI agents working in this Bun monorepo. ## Project Overview - **Monorepo**: Bun workspaces + Turborepo orchestration - **Runtime**: Bun (see `mise.toml` for version) - **Apps**: - `apps/server` - TanStack Start fullstack web app (see `apps/server/AGENTS.md`) - `apps/desktop` - Tauri v2 desktop shell, loads server via sidecar (see `apps/desktop/AGENTS.md`) - **Packages**: `packages/utils`, `packages/tsconfig` (shared configs) ## Build / Lint / Test Commands ### Root Commands (via Turbo) ```bash bun dev # Start all apps in dev mode bun build # Build all apps bun fix # Lint + format (Biome auto-fix) bun typecheck # TypeScript check across monorepo ``` ### Server App (`apps/server`) ```bash bun dev # Vite dev server (localhost:3000) bun build # Production build → .output/ bun fix # Biome auto-fix bun typecheck # TypeScript check # Database (Drizzle) bun db:generate # Generate migrations from schema bun db:migrate # Run migrations bun db:push # Push schema (dev only) bun db:studio # Open Drizzle Studio ``` ### Desktop App (`apps/desktop`) ```bash bun dev # Copy sidecar + start Tauri dev bun build # Copy sidecar + build installer # Rust (from apps/desktop/src-tauri/) cargo check # Compile check cargo clippy # Linter cargo fmt # Formatter ``` ### Testing No test framework configured yet. When adding tests: ```bash bun test path/to/test.ts # Run single test file bun test -t "pattern" # Run tests matching pattern cargo test test_name -- --nocapture # Rust single test with output ``` ## Code Style (TypeScript) ### Formatting (Biome) - **Indent**: 2 spaces - **Line endings**: LF - **Quotes**: Single `'` - **Semicolons**: Omit (ASI) - **Arrow parentheses**: Always `(x) => x` ### Imports Biome auto-organizes. Order: 1. External packages 2. Internal `@/*` aliases 3. Type imports (`import type { ... }`) ```typescript import { createFileRoute } from '@tanstack/react-router' import { z } from 'zod' import { db } from '@/server/db' import type { ReactNode } from 'react' ``` ### TypeScript Strictness - `strict: true` - `noUncheckedIndexedAccess: true` - array/object access returns `T | undefined` - `noImplicitOverride: true` - `verbatimModuleSyntax: true` - Use `@/*` path aliases (maps to `src/*`) ### Naming Conventions | Type | Convention | Example | |------|------------|---------| | Files (utils) | kebab-case | `auth-utils.ts` | | Files (components) | PascalCase | `UserProfile.tsx` | | Components | PascalCase arrow | `const Button = () => {}` | | Functions | camelCase | `getUserById` | | Constants | UPPER_SNAKE | `MAX_RETRIES` | | Types/Interfaces | PascalCase | `UserProfile` | ### React Patterns ```typescript // Components: arrow functions (enforced by Biome) const MyComponent = ({ title }: { title: string }) => { return
{title}
} // Routes: TanStack Router file conventions export const Route = createFileRoute('/')({ component: Home, }) // Data fetching: TanStack Query const { data } = useSuspenseQuery(orpc.todo.list.queryOptions()) ``` ### Error Handling - Use `try-catch` for async operations - Throw descriptive errors - ORPC: Use `ORPCError` with proper codes (`NOT_FOUND`, `INPUT_VALIDATION_FAILED`) - Never use empty catch blocks ## Code Style (Rust - Tauri) - **Indent**: 4 spaces - **Naming**: snake_case (functions), PascalCase (types), SCREAMING_SNAKE (consts) - Use `expect("中文消息")` over `unwrap()` - Async: `tokio` runtime, `tauri::async_runtime::spawn` - Run `cargo fmt` and `cargo clippy` before commit ## Database (Drizzle ORM) ```typescript import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core' import { sql } from 'drizzle-orm' export const myTable = pgTable('my_table', { id: uuid().primaryKey().default(sql`uuidv7()`), name: text().notNull(), createdAt: timestamp({ withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp({ withTimezone: true }).notNull().defaultNow().$onUpdateFn(() => new Date()), }) ``` ## Environment Variables - Use `@t3-oss/env-core` with Zod validation in `src/env.ts` - Server vars: no prefix - Client vars: `VITE_` prefix required - Never commit `.env` files ## Critical Rules **DO:** - Run `bun fix` before committing - Use `@/*` path aliases (not relative imports) - Let React Compiler handle memoization (no manual `useMemo`/`useCallback`) - Include `createdAt`/`updatedAt` on all tables **DON'T:** - Edit `src/routeTree.gen.ts` (auto-generated) - Use `as any`, `@ts-ignore`, `@ts-expect-error` - Commit `.env` files - Use empty catch blocks `catch(e) {}` - Use `unwrap()` in Rust without `expect()` ## Git Workflow 1. Make changes following style guide 2. `bun fix` - auto-format and lint 3. `bun typecheck` - verify types 4. `bun dev` - test locally 5. Commit with descriptive message ## Directory Structure ``` . ├── apps/ │ ├── server/ # TanStack Start fullstack app │ │ ├── src/ │ │ │ ├── client/ # ORPC client, Query client │ │ │ ├── components/ │ │ │ ├── routes/ # File-based routing │ │ │ └── server/ # API layer + database │ │ │ ├── api/ # ORPC contracts, routers, middlewares │ │ │ └── db/ # Drizzle schema │ │ └── AGENTS.md │ └── desktop/ # Tauri v2 shell (no frontend src) │ ├── src-tauri/ # Rust Tauri code │ │ ├── src/ # Rust source │ │ └── binaries/ # Sidecar binaries │ └── AGENTS.md ├── packages/ │ ├── tsconfig/ # Shared TS configs │ └── utils/ # Shared utilities ├── biome.json # Linting/formatting config ├── turbo.json # Turbo task orchestration └── package.json # Workspace root ``` ## See Also - `apps/server/AGENTS.md` - Detailed TanStack Start / ORPC patterns - `apps/desktop/AGENTS.md` - Rust / Tauri development guide