# CLAUDE.md — Mosaic Stack

## Project

Self-hosted, multi-user AI agent platform. TypeScript monorepo.

## Stack

- **API**: NestJS + Fastify adapter (`apps/gateway`)
- **Web**: Next.js 16 + React 19 (`apps/web`)
- **ORM**: Drizzle ORM + PostgreSQL 17 + pgvector (`packages/db`)
- **Auth**: BetterAuth (`packages/auth`)
- **Agent**: Pi SDK (`packages/agent`, `packages/mosaic`)
- **Queue**: Valkey 8 (`packages/queue`)
- **Build**: pnpm workspaces + Turborepo
- **CI**: Woodpecker CI
- **Observability**: OpenTelemetry → Jaeger

## Commands

```bash
pnpm typecheck        # TypeScript check (all packages)
pnpm lint             # ESLint (all packages)
pnpm format:check     # Prettier check
pnpm test             # Vitest (all packages)
pnpm build            # Build all packages

# Database
pnpm --filter @mosaicstack/db db:push      # Push schema to PG (dev)
pnpm --filter @mosaicstack/db db:generate  # Generate migrations
pnpm --filter @mosaicstack/db db:migrate   # Run migrations

# Dev
docker compose up -d                  # Start PG, Valkey, OTEL, Jaeger
pnpm --filter @mosaicstack/gateway exec tsx src/main.ts  # Start gateway
```

## Conventions

- ESM everywhere (`"type": "module"`, `.js` extensions in imports)
- NodeNext module resolution
- Explicit `@Inject()` decorators in NestJS (tsx/esbuild doesn't support emitDecoratorMetadata)
- DTOs in `*.dto.ts` files at module boundaries
- OTEL tracing imported before NestJS bootstrap (`import './tracing.js'`)
- All three gates must pass before push: typecheck, lint, format:check
