# Veridian AGENTS.md **Nuxt 4 AI chat platform with Drizzle + PostgreSQL + better-auth** ## Architecture Overview ``` app/ # Nuxt 4 application (SSR) ├── components/ # Vue components (Sidenav, Message, Settings, etc.) ├── composables/ # Shared state (useChat, useAuth, useModels, etc.) ├── layouts/ # Auth and default layouts ├── middleware/ # Global auth middleware ├── pages/ # File-based routing (/, /auth/*, /agent/*) ├── plugins/ # Auth plugins (client/server) ├── types/ # TypeScript interfaces └── utils/ # Crypto, model-mapping, search utilities server/ # Server-side API routes └── api/ ├── auth/ # Better-auth handler ├── chat/ # Generation, cancel endpoints └── provider/ # Model fetching from providers drizzle/ # Database (migrated from Triplit) ├── schema.ts # Full schema with auth + app tables ├── relations.ts # Table relationships └── migrations/ # SQL migration files lib/ # Shared utilities ├── auth.ts # Better-auth server config └── auth-client.ts # Better-auth client ``` ## Core Commands As an agentic agent, never run the development server yourself. Instead, once you have completed your task, end generation and inform me that you are done and request a review of your changes. You are allowed to use database commands. ### Database Commands - `bunx drizzle-kit push` - Push schema changes to database - `bunx drizzle-kit generate` - Generate migration from schema changes - `bunx drizzle-kit migrate` - Run pending migrations ## Code Style - **Indentation**: 4 spaces (no tabs) - **Semicolons**: Always required - **Equality**: Strict equality only (`===`, `!==`) - **Components**: PascalCase, never self-closing tags - **Functions**: camelCase for functions, PascalCase for constructors/classes - **Constants**: SCREAMING_SNAKE_CASE for constants, camelCase for const values - **Files**: kebab-case for files, PascalCase for Vue components - **State**: `useState('prefix:name', () => default)` for reactive state ## Imports Order Group imports in this order (alphabetical within groups): 1. Type imports (`types/`) 2. Dependency imports (npm packages) 3. Vue/Nuxt imports (`vue`, `#app`, `~~/`, `~/`, `@/`) 4. Local imports (`lib/`, `server/`) ```typescript // Example import order import type { Result, Ok, Err } from '~~/types/result'; import type { Foo } from 'vue'; import { ref, computed } from 'vue'; import { useFeature } from '~/composables/useFeature'; import { decrypt } from '~/utils/crypto'; import { authClient } from '~~/lib/auth-client'; ``` ## Error Handling Avoid using `throw` statements in your code. Use the `Result` type with `Ok()` and `Err()` factory functions: ```typescript import { type Result, Ok, Err } from '~~/types/result'; const myFunction(): Result { if (error) { return Err(ErrorType.SpecificError); } return Ok(data); } // Usage const result = myFunction(); if (result.ok === false) { console.error(result.error); return; } processData(result.data); ``` ## Composables Pattern All composables return reactive state + actions: ```typescript export const useFeature = () => { const state = useState('feature:state', () => defaultValue); const computedValue = computed(() => /* logic */); const actions = { async doSomething() { // implementation }, }; return { state, computedValue, ...actions }; }; ``` ## API Route Pattern ```typescript // File: server/api/endpoint.method.ts import { protectRoute } from '~/server/utils/protect'; export default defineEventHandler(async (event) => { await protectRoute(event); // implementation return { /* response */ }; }); ``` ## Database Query Pattern (Drizzle) ```typescript // Server-side queries use Drizzle ORM import { db } from '~~/server/db'; import { agents } from '~~/drizzle/schema'; // Query with relations const result = await db.query.agents.findMany({ where: eq(agents.userId, userId), with: { topics: true, messages: true }, }); // Insert with conflict handling await db.insert(agents).values(data).onConflictDoNothing(); ``` ## Authentication Flow 1. **Login**: Client uses `authClient.signIn.email()` → derives encryption key from password 2. **Session**: Better-auth creates session token → stored in cookie 3. **API Keys**: Encrypted client-side with AES-GCM (key derived from password + userId) **Key files**: `lib/auth.ts`, `app/plugins/auth.*.ts`, `app/middleware/auth.global.ts`, `app/composables/useAuth.ts` ## Styling System **UnoCSS** with semantic color variables in `app/assets/css/base.css`: - `--color-accent`, `--color-neutral`, `--color-text`, `--color-muted` - `--color-highlight` (for borders/hover states) - Theme-aware: `:root.dark` / `:root.light` with `color-mix()` ## Environment Variables | Variable | Purpose | |----------|---------| | `DATABASE_URL` | PostgreSQL connection string | | `BETTER_AUTH_SECRET` | Auth encryption | ## Key Composables | Composable | Purpose | |------------|---------| | `useAuth()` | Session/user state, signIn/signOut | | `useChat(id)` | Send/regenerate messages, create topics | | `useModels()` | Providers + models with auto-subscription | | `useAgents()` | User's agents with topics | | `useSidebar()` | Sidebar state + resize | | `useTheme()` | Accent/neutral theme cookies | | `useSettings()` | Settings dialog state | ## Development Notes - **Never run dev server as agent** - just complete tasks and signal done - Always run `bunx drizzle-kit push` after schema changes - Use `protectRoute()` in all API routes for auth - All UI state that persists → use cookies (`useCookie()`) - Real-time data → SSE events via `server/utils/events.ts` - API keys → encrypted client-side before DB storage