+
+
+ ))
+ .listen(3000)
+```
+
+This plugin will automatically add `Content-Type: text/html; charset=utf8` header to the response, add ``, and convert it into a Response object.
+
+## JSX
+Elysia can use JSX
+
+1. Replace your file that needs to use JSX to end with affix **"x"**:
+- .js -> .jsx
+- .ts -> .tsx
+
+2. Register the TypeScript type by append the following to **tsconfig.json**:
+```jsonc
+// tsconfig.json
+{
+ "compilerOptions": {
+ "jsx": "react",
+ "jsxFactory": "Html.createElement",
+ "jsxFragmentFactory": "Html.Fragment"
+ }
+}
+```
+
+3. Starts using JSX in your file
+```tsx twoslash
+import React from 'react'
+import { Elysia } from 'elysia'
+import { html, Html } from '@elysiajs/html'
+
+new Elysia()
+ .use(html())
+ .get('/', () => (
+
+
+ Hello World
+
+
+
Hello World
+
+
+ ))
+ .listen(3000)
+```
+
+If the error `Cannot find name 'Html'. Did you mean 'html'?` occurs, this import must be added to the JSX template:
+
+```tsx
+import { Html } from '@elysiajs/html'
+```
+
+It is important that it is written in uppercase.
+
+## XSS
+
+Elysia HTML is based use of the Kita HTML plugin to detect possible XSS attacks in compile time.
+
+You can use a dedicated `safe` attribute to sanitize user value to prevent XSS vulnerability.
+
+```tsx
+import { Elysia, t } from 'elysia'
+import { html, Html } from '@elysiajs/html'
+
+new Elysia()
+ .use(html())
+ .post(
+ '/',
+ ({ body }) => (
+
+
+ Hello World
+
+
+
{body}
+
+
+ ),
+ {
+ body: t.String()
+ }
+ )
+ .listen(3000)
+```
+
+However, when are building a large-scale app, it's best to have a type reminder to detect possible XSS vulnerabilities in your codebase.
+
+To add a type-safe reminder, please install:
+
+```sh
+bun add @kitajs/ts-html-plugin
+```
+
+Then appends the following **tsconfig.json**
+
+```jsonc
+// tsconfig.json
+{
+ "compilerOptions": {
+ "jsx": "react",
+ "jsxFactory": "Html.createElement",
+ "jsxFragmentFactory": "Html.Fragment",
+ "plugins": [{ "name": "@kitajs/ts-html-plugin" }]
+ }
+}
+```
+
+## Config
+Below is a config which is accepted by the plugin
+
+### contentType
+
+- Type: `string`
+- Default: `'text/html; charset=utf8'`
+
+The content-type of the response.
+
+### autoDetect
+
+- Type: `boolean`
+- Default: `true`
+
+Whether to automatically detect HTML content and set the content-type.
+
+### autoDoctype
+
+- Type: `boolean | 'full'`
+- Default: `true`
+
+Whether to automatically add `` to a response starting with ``, if not found.
+
+Use `full` to also automatically add doctypes on responses returned without this plugin
+
+```ts
+// without the plugin
+app.get('/', () => '')
+
+// With the plugin
+app.get('/', ({ html }) => html(''))
+```
+
+### isHtml
+
+- Type: `(value: string) => boolean`
+- Default: `isHtml` (exported function)
+
+The function is used to detect if a string is a html or not. Default implementation if length is greater than 7, starts with `<` and ends with `>`.
+
+Keep in mind there's no real way to validate HTML, so the default implementation is a best guess.
diff --git a/.agent/skills/tech-stack/elysiajs/plugins/jwt.md b/.agent/skills/tech-stack/elysiajs/plugins/jwt.md
new file mode 100644
index 0000000..b5767bf
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/plugins/jwt.md
@@ -0,0 +1,197 @@
+# JWT Plugin
+This plugin adds support for using JWT in Elysia handlers.
+
+## Installation
+```bash
+bun add @elysiajs/jwt
+```
+
+## Basic Usage
+```typescript [cookie]
+import { Elysia } from 'elysia'
+import { jwt } from '@elysiajs/jwt'
+
+const app = new Elysia()
+ .use(
+ jwt({
+ name: 'jwt',
+ secret: 'Fischl von Luftschloss Narfidort'
+ })
+ )
+ .get('/sign/:name', async ({ jwt, params: { name }, cookie: { auth } }) => {
+ const value = await jwt.sign({ name })
+
+ auth.set({
+ value,
+ httpOnly: true,
+ maxAge: 7 * 86400,
+ path: '/profile',
+ })
+
+ return `Sign in as ${value}`
+ })
+ .get('/profile', async ({ jwt, status, cookie: { auth } }) => {
+ const profile = await jwt.verify(auth.value)
+
+ if (!profile)
+ return status(401, 'Unauthorized')
+
+ return `Hello ${profile.name}`
+ })
+ .listen(3000)
+```
+
+## Config
+This plugin extends config from [jose](https://github.com/panva/jose).
+
+Below is a config that is accepted by the plugin.
+
+### name
+Name to register `jwt` function as.
+
+For example, `jwt` function will be registered with a custom name.
+```typescript
+new Elysia()
+ .use(
+ jwt({
+ name: 'myJWTNamespace',
+ secret: process.env.JWT_SECRETS!
+ })
+ )
+ .get('/sign/:name', ({ myJWTNamespace, params }) => {
+ return myJWTNamespace.sign(params)
+ })
+```
+
+Because some might need to use multiple `jwt` with different configs in a single server, explicitly registering the JWT function with a different name is needed.
+
+### secret
+The private key to sign JWT payload with.
+
+### schema
+Type strict validation for JWT payload.
+
+### alg
+@default `HS256`
+
+Signing Algorithm to sign JWT payload with.
+
+Possible properties for jose are:
+HS256
+HS384
+HS512
+PS256
+PS384
+PS512
+RS256
+RS384
+RS512
+ES256
+ES256K
+ES384
+ES512
+EdDSA
+
+### iss
+The issuer claim identifies the principal that issued the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1)
+
+TLDR; is usually (the domain) name of the signer.
+
+### sub
+The subject claim identifies the principal that is the subject of the JWT.
+
+The claims in a JWT are normally statements about the subject as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2)
+
+### aud
+The audience claim identifies the recipients that the JWT is intended for.
+
+Each principal intended to process the JWT MUST identify itself with a value in the audience claim as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3)
+
+### jti
+JWT ID claim provides a unique identifier for the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7)
+
+### nbf
+The "not before" claim identifies the time before which the JWT must not be accepted for processing as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5)
+
+### exp
+The expiration time claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4)
+
+### iat
+The "issued at" claim identifies the time at which the JWT was issued.
+
+This claim can be used to determine the age of the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6)
+
+### b64
+This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing input computation as per [RFC7797](https://www.rfc-editor.org/rfc/rfc7797).
+
+### kid
+A hint indicating which key was used to secure the JWS.
+
+This parameter allows originators to explicitly signal a change of key to recipients as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.4)
+
+### x5t
+(X.509 certificate SHA-1 thumbprint) header parameter is a base64url-encoded SHA-1 digest of the DER encoding of the X.509 certificate [RFC5280](https://www.rfc-editor.org/rfc/rfc5280) corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.7)
+
+### x5c
+(X.509 certificate chain) header parameter contains the X.509 public key certificate or certificate chain [RFC5280](https://www.rfc-editor.org/rfc/rfc5280) corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.6)
+
+### x5u
+(X.509 URL) header parameter is a URI [RFC3986](https://www.rfc-editor.org/rfc/rfc3986) that refers to a resource for the X.509 public key certificate or certificate chain [RFC5280] corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.5)
+
+### jwk
+The "jku" (JWK Set URL) Header Parameter is a URI [RFC3986] that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used to digitally sign the JWS.
+
+The keys MUST be encoded as a JWK Set [JWK] as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.2)
+
+### typ
+The `typ` (type) Header Parameter is used by JWS applications to declare the media type [IANA.MediaTypes] of this complete JWS.
+
+This is intended for use by the application when more than one kind of object could be present in an application data structure that can contain a JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.9)
+
+### ctr
+Content-Type parameter is used by JWS applications to declare the media type [IANA.MediaTypes] of the secured content (the payload).
+
+This is intended for use by the application when more than one kind of object could be present in the JWS Payload as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.9)
+
+## Handler
+Below are the value added to the handler.
+
+### jwt.sign
+A dynamic object of collection related to use with JWT registered by the JWT plugin.
+
+Type:
+```typescript
+sign: (payload: JWTPayloadSpec): Promise
+```
+
+`JWTPayloadSpec` accepts the same value as [JWT config](#config)
+
+### jwt.verify
+Verify payload with the provided JWT config
+
+Type:
+```typescript
+verify(payload: string) => Promise
+```
+
+`JWTPayloadSpec` accepts the same value as [JWT config](#config)
+
+## Pattern
+Below you can find the common patterns to use the plugin.
+
+## Set JWT expiration date
+By default, the config is passed to `setCookie` and inherits its value.
+
+```typescript
+const app = new Elysia()
+ .use(
+ jwt({
+ name: 'jwt',
+ secret: 'kunikuzushi',
+ exp: '7d'
+ })
+ )
+ .get('/sign/:name', async ({ jwt, params }) => jwt.sign(params))
+```
+
+This will sign JWT with an expiration date of the next 7 days.
diff --git a/.agent/skills/tech-stack/elysiajs/plugins/openapi.md b/.agent/skills/tech-stack/elysiajs/plugins/openapi.md
new file mode 100644
index 0000000..c69150d
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/plugins/openapi.md
@@ -0,0 +1,246 @@
+# OpenAPI Plugin
+
+## Installation
+```bash
+bun add @elysiajs/openapi
+```
+
+## Basic Usage
+```typescript
+import { openapi } from '@elysiajs/openapi'
+
+new Elysia()
+ .use(openapi())
+ .get('/', () => 'hello')
+```
+
+Docs at `/openapi`, spec at `/openapi/json`.
+
+## Detail Object
+Extends OpenAPI Operation Object:
+```typescript
+.get('/', () => 'hello', {
+ detail: {
+ title: 'Hello',
+ description: 'An example route',
+ summary: 'Short summary',
+ deprecated: false,
+ hide: true, // Hide from docs
+ tags: ['App']
+ }
+})
+```
+
+### Documentation Config
+```typescript
+openapi({
+ documentation: {
+ info: {
+ title: 'API',
+ version: '1.0.0'
+ },
+ tags: [
+ { name: 'App', description: 'General' }
+ ],
+ components: {
+ securitySchemes: {
+ bearerAuth: { type: 'http', scheme: 'bearer' }
+ }
+ }
+ }
+})
+```
+
+### Standard Schema Mapping
+```typescript
+mapJsonSchema: {
+ zod: z.toJSONSchema, // Zod 4
+ valibot: toJsonSchema,
+ effect: JSONSchema.make
+}
+```
+
+Zod 3: `zodToJsonSchema` from `zod-to-json-schema`
+
+## OpenAPI Type Gen
+Generate docs from types:
+```typescript
+import { fromTypes } from '@elysiajs/openapi'
+
+export const app = new Elysia()
+ .use(openapi({
+ references: fromTypes()
+ }))
+```
+
+### Production
+Recommended to generate `.d.ts` file for production when using OpenAPI Type Gen
+```typescript
+references: fromTypes(
+ process.env.NODE_ENV === 'production'
+ ? 'dist/index.d.ts'
+ : 'src/index.ts'
+)
+```
+
+### Options
+```typescript
+fromTypes('src/index.ts', {
+ projectRoot: path.join('..', import.meta.dir),
+ tsconfigPath: 'tsconfig.dts.json'
+})
+```
+
+### Caveat: Explicit Types
+Use `Prettify` helper to inline when type is not showing:
+```typescript
+type Prettify = { [K in keyof T]: T[K] } & {}
+
+function getUser(): Prettify { }
+```
+
+## Schema Description
+```typescript
+body: t.Object({
+ username: t.String(),
+ password: t.String({
+ minLength: 8,
+ description: 'Password (8+ chars)'
+ })
+}, {
+ description: 'Expected username and password'
+}),
+detail: {
+ summary: 'Sign in user',
+ tags: ['auth']
+}
+```
+
+## Response Headers
+```typescript
+import { withHeader } from '@elysiajs/openapi'
+
+response: withHeader(
+ t.Literal('Hi'),
+ { 'x-powered-by': t.Literal('Elysia') }
+)
+```
+
+Annotation only - doesn't enforce. Set headers manually.
+
+## Tags
+Define + assign:
+```typescript
+.use(openapi({
+ documentation: {
+ tags: [
+ { name: 'App', description: 'General' },
+ { name: 'Auth', description: 'Auth' }
+ ]
+ }
+}))
+.get('/', () => 'hello', {
+ detail: { tags: ['App'] }
+})
+```
+
+### Instance Tags
+```typescript
+new Elysia({ tags: ['user'] })
+ .get('/user', 'user')
+```
+
+## Reference Models
+Auto-generates schemas:
+```typescript
+.model({
+ User: t.Object({
+ id: t.Number(),
+ username: t.String()
+ })
+})
+.get('/user', () => ({ id: 1, username: 'x' }), {
+ response: { 200: 'User' },
+ detail: { tags: ['User'] }
+})
+```
+
+## Guard
+Apply to instance/group:
+```typescript
+.guard({
+ detail: {
+ description: 'Requires auth'
+ }
+})
+.get('/user', 'user')
+```
+
+## Security
+```typescript
+.use(openapi({
+ documentation: {
+ components: {
+ securitySchemes: {
+ bearerAuth: {
+ type: 'http',
+ scheme: 'bearer',
+ bearerFormat: 'JWT'
+ }
+ }
+ }
+ }
+}))
+
+new Elysia({
+ prefix: '/address',
+ detail: {
+ security: [{ bearerAuth: [] }]
+ }
+})
+```
+
+Secures all routes under prefix.
+
+## Config
+Below is a config which is accepted by the `openapi({})`
+
+### enabled
+@default true
+Enable/Disable the plugin
+
+### documentation
+OpenAPI documentation information
+@see https://spec.openapis.org/oas/v3.0.3.html
+
+### exclude
+Configuration to exclude paths or methods from documentation
+
+### exclude.methods
+List of methods to exclude from documentation
+
+### exclude.paths
+List of paths to exclude from documentation
+
+### exclude.staticFile
+@default true
+
+Exclude static file routes from documentation
+
+### exclude.tags
+List of tags to exclude from documentation
+
+### mapJsonSchema
+A custom mapping function from Standard schema to OpenAPI schema
+
+### path
+@default '/openapi'
+The endpoint to expose OpenAPI documentation frontend
+
+### provider
+@default 'scalar'
+
+OpenAPI documentation frontend between:
+- Scalar
+- SwaggerUI
+- null: disable frontend
diff --git a/.agent/skills/tech-stack/elysiajs/plugins/opentelemetry.md b/.agent/skills/tech-stack/elysiajs/plugins/opentelemetry.md
new file mode 100644
index 0000000..0ca95c3
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/plugins/opentelemetry.md
@@ -0,0 +1,167 @@
+# OpenTelemetry Plugin - SKILLS.md
+
+## Installation
+```bash
+bun add @elysiajs/opentelemetry
+```
+
+## Basic Usage
+```typescript
+import { opentelemetry } from '@elysiajs/opentelemetry'
+import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
+
+new Elysia()
+ .use(opentelemetry({
+ spanProcessors: [
+ new BatchSpanProcessor(new OTLPTraceExporter())
+ ]
+ }))
+```
+
+Auto-collects spans from OpenTelemetry-compatible libraries. Parent/child spans applied automatically.
+
+## Config
+Extends OpenTelemetry SDK params:
+
+- `autoDetectResources` (true) - Auto-detect from env
+- `contextManager` (AsyncHooksContextManager) - Custom context
+- `textMapPropagator` (CompositePropagator) - W3C Trace + Baggage
+- `metricReader` - For MeterProvider
+- `views` - Histogram bucket config
+- `instrumentations` (getNodeAutoInstrumentations()) - Metapackage or individual
+- `resource` - Custom resource
+- `resourceDetectors` ([envDetector, processDetector, hostDetector]) - Auto-detect needs `autoDetectResources: true`
+- `sampler` - Custom sampler (default: sample all)
+- `serviceName` - Namespace identifier
+- `spanProcessors` - Array for tracer provider
+- `traceExporter` - Auto-setup OTLP/http/protobuf with BatchSpanProcessor if not set
+- `spanLimits` - Tracing params
+
+### Resource Detectors via Env
+```bash
+export OTEL_NODE_RESOURCE_DETECTORS="env,host"
+# Options: env, host, os, process, serviceinstance, all, none
+```
+
+## Export to Backends
+Example - Axiom:
+```typescript
+.use(opentelemetry({
+ spanProcessors: [
+ new BatchSpanProcessor(
+ new OTLPTraceExporter({
+ url: 'https://api.axiom.co/v1/traces',
+ headers: {
+ Authorization: `Bearer ${Bun.env.AXIOM_TOKEN}`,
+ 'X-Axiom-Dataset': Bun.env.AXIOM_DATASET
+ }
+ })
+ )
+ ]
+}))
+```
+
+## OpenTelemetry SDK
+Use SDK normally - runs under Elysia's request span, auto-appears in trace.
+
+## Record Utility
+Equivalent to `startActiveSpan` - auto-closes + captures exceptions:
+```typescript
+import { record } from '@elysiajs/opentelemetry'
+
+.get('', () => {
+ return record('database.query', () => {
+ return db.query('SELECT * FROM users')
+ })
+})
+```
+
+Label for code shown in trace.
+
+## Function Naming
+Elysia reads function names as span names:
+```typescript
+// ⚠️ Anonymous span
+.derive(async ({ cookie: { session } }) => {
+ return { user: await getProfile(session) }
+})
+
+// ✅ Named span: "getProfile"
+.derive(async function getProfile({ cookie: { session } }) {
+ return { user: await getProfile(session) }
+})
+```
+
+## getCurrentSpan
+Get current span outside handler (via AsyncLocalStorage):
+```typescript
+import { getCurrentSpan } from '@elysiajs/opentelemetry'
+
+function utility() {
+ const span = getCurrentSpan()
+ span.setAttributes({ 'custom.attribute': 'value' })
+}
+```
+
+## setAttributes
+Sugar for `getCurrentSpan().setAttributes`:
+```typescript
+import { setAttributes } from '@elysiajs/opentelemetry'
+
+function utility() {
+ setAttributes({ 'custom.attribute': 'value' })
+}
+```
+
+## Instrumentations (Advanced)
+SDK must run before importing instrumented module.
+
+### Setup
+1. Separate file:
+```typescript
+// src/instrumentation.ts
+import { opentelemetry } from '@elysiajs/opentelemetry'
+import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'
+
+export const instrumentation = opentelemetry({
+ instrumentations: [new PgInstrumentation()]
+})
+```
+
+2. Apply:
+```typescript
+// src/index.ts
+import { instrumentation } from './instrumentation'
+new Elysia().use(instrumentation).listen(3000)
+```
+
+3. Preload:
+```toml
+# bunfig.toml
+preload = ["./src/instrumentation.ts"]
+```
+
+### Production Deployment (Advanced)
+OpenTelemetry monkey-patches `node_modules`. Exclude instrumented libs from bundling:
+```bash
+bun build --compile --external pg --outfile server src/index.ts
+```
+
+Package.json:
+```json
+{
+ "dependencies": { "pg": "^8.15.6" },
+ "devDependencies": {
+ "@elysiajs/opentelemetry": "^1.2.0",
+ "@opentelemetry/instrumentation-pg": "^0.52.0"
+ }
+}
+```
+
+Production install:
+```bash
+bun install --production
+```
+
+Keeps `node_modules` with instrumented libs at runtime.
diff --git a/.agent/skills/tech-stack/elysiajs/plugins/server-timing.md b/.agent/skills/tech-stack/elysiajs/plugins/server-timing.md
new file mode 100644
index 0000000..0021424
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/plugins/server-timing.md
@@ -0,0 +1,71 @@
+# Server Timing Plugin
+This plugin adds support for auditing performance bottlenecks with Server Timing API
+
+## Installation
+```bash
+bun add @elysiajs/server-timing
+```
+
+## Basic Usage
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { serverTiming } from '@elysiajs/server-timing'
+
+new Elysia()
+ .use(serverTiming())
+ .get('/', () => 'hello')
+ .listen(3000)
+```
+
+Server Timing then will append header 'Server-Timing' with log duration, function name, and detail for each life-cycle function.
+
+To inspect, open browser developer tools > Network > [Request made through Elysia server] > Timing.
+
+Now you can effortlessly audit the performance bottleneck of your server.
+
+## Config
+Below is a config which is accepted by the plugin
+
+### enabled
+@default `NODE_ENV !== 'production'`
+
+Determine whether or not Server Timing should be enabled
+
+### allow
+@default `undefined`
+
+A condition whether server timing should be log
+
+### trace
+@default `undefined`
+
+Allow Server Timing to log specified life-cycle events:
+
+Trace accepts objects of the following:
+- request: capture duration from request
+- parse: capture duration from parse
+- transform: capture duration from transform
+- beforeHandle: capture duration from beforeHandle
+- handle: capture duration from the handle
+- afterHandle: capture duration from afterHandle
+- total: capture total duration from start to finish
+
+## Pattern
+Below you can find the common patterns to use the plugin.
+
+## Allow Condition
+You may disable Server Timing on specific routes via `allow` property
+
+```ts twoslash
+import { Elysia } from 'elysia'
+import { serverTiming } from '@elysiajs/server-timing'
+
+new Elysia()
+ .use(
+ serverTiming({
+ allow: ({ request }) => {
+ return new URL(request.url).pathname !== '/no-trace'
+ }
+ })
+ )
+```
diff --git a/.agent/skills/tech-stack/elysiajs/plugins/static.md b/.agent/skills/tech-stack/elysiajs/plugins/static.md
new file mode 100644
index 0000000..82fa1da
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/plugins/static.md
@@ -0,0 +1,84 @@
+# Static Plugin
+This plugin can serve static files/folders for Elysia Server
+
+## Installation
+```bash
+bun add @elysiajs/static
+```
+
+## Basic Usage
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { staticPlugin } from '@elysiajs/static'
+
+new Elysia()
+ .use(staticPlugin())
+ .listen(3000)
+```
+
+By default, the static plugin default folder is `public`, and registered with `/public` prefix.
+
+Suppose your project structure is:
+```
+| - src
+ | - index.ts
+| - public
+ | - takodachi.png
+ | - nested
+ | - takodachi.png
+```
+
+The available path will become:
+- /public/takodachi.png
+- /public/nested/takodachi.png
+
+## Config
+Below is a config which is accepted by the plugin
+
+### assets
+@default `"public"`
+
+Path to the folder to expose as static
+
+### prefix
+@default `"/public"`
+
+Path prefix to register public files
+
+### ignorePatterns
+@default `[]`
+
+List of files to ignore from serving as static files
+
+### staticLimit
+@default `1024`
+
+By default, the static plugin will register paths to the Router with a static name, if the limits are exceeded, paths will be lazily added to the Router to reduce memory usage.
+Tradeoff memory with performance.
+
+### alwaysStatic
+@default `false`
+
+If set to true, static files path will be registered to Router skipping the `staticLimits`.
+
+### headers
+@default `{}`
+
+Set response headers of files
+
+### indexHTML
+@default `false`
+
+If set to true, the `index.html` file from the static directory will be served for any request that is matching neither a route nor any existing static file.
+
+## Pattern
+Below you can find the common patterns to use the plugin.
+
+## Single file
+Suppose you want to return just a single file, you can use `file` instead of using the static plugin
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia()
+ .get('/file', file('public/takodachi.png'))
+```
diff --git a/.agent/skills/tech-stack/elysiajs/references/bun-fullstack-dev-server.md b/.agent/skills/tech-stack/elysiajs/references/bun-fullstack-dev-server.md
new file mode 100644
index 0000000..70d721b
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/bun-fullstack-dev-server.md
@@ -0,0 +1,129 @@
+# Fullstack Dev Server
+
+## What It Is
+Bun 1.3 Fullstack Dev Server with HMR. React without bundler (no Vite/Webpack).
+
+Example: [elysia-fullstack-example](https://github.com/saltyaom/elysia-fullstack-example)
+
+## Setup
+1. Install + use Elysia Static:
+```typescript
+import { Elysia } from 'elysia'
+import { staticPlugin } from '@elysiajs/static'
+
+new Elysia()
+ .use(await staticPlugin()) // await required for HMR hooks
+ .listen(3000)
+```
+
+2. Create `public/index.html` + `public/index.tsx`:
+```html
+
+
+
+
+
+ Elysia React App
+
+
+
+
+
+
+
+```
+
+```tsx
+// public/index.tsx
+import { useState } from 'react'
+import { createRoot } from 'react-dom/client'
+
+function App() {
+ const [count, setCount] = useState(0)
+ const increase = () => setCount((c) => c + 1)
+
+ return (
+
+
{count}
+
+
+ )
+}
+
+const root = createRoot(document.getElementById('root')!)
+root.render()
+```
+
+3. Enable JSX in `tsconfig.json`:
+```json
+{
+ "compilerOptions": {
+ "jsx": "react-jsx"
+ }
+}
+```
+
+4. Navigate to `http://localhost:3000/public`.
+
+Frontend + backend in single project. No bundler. Works with HMR, Tailwind, Tanstack Query, Eden Treaty, path alias.
+
+## Custom Prefix
+```typescript
+.use(await staticPlugin({ prefix: '/' }))
+```
+
+Serves at `/` instead of `/public`.
+
+## Tailwind CSS
+1. Install:
+```bash
+bun add tailwindcss@4
+bun add -d bun-plugin-tailwind
+```
+
+2. Create `bunfig.toml`:
+```toml
+[serve.static]
+plugins = ["bun-plugin-tailwind"]
+```
+
+3. Create `public/global.css`:
+```css
+@tailwind base;
+```
+
+4. Add to HTML or TS:
+```html
+
+```
+Or:
+```tsx
+import './global.css'
+```
+
+## Path Alias
+1. Add to `tsconfig.json`:
+```json
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@public/*": ["public/*"]
+ }
+ }
+}
+```
+
+2. Use:
+```tsx
+import '@public/global.css'
+```
+
+Works out of box.
+
+## Production Build
+```bash
+bun build --compile --target bun --outfile server src/index.ts
+```
+
+Creates single executable `server`. Include `public` folder when running.
diff --git a/.agent/skills/tech-stack/elysiajs/references/cookie.md b/.agent/skills/tech-stack/elysiajs/references/cookie.md
new file mode 100644
index 0000000..9e1aa1c
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/cookie.md
@@ -0,0 +1,187 @@
+# Cookie
+
+## What It Is
+Reactive mutable signal for cookie interaction. Auto-encodes/decodes objects.
+
+## Basic Usage
+No get/set - direct value access:
+```typescript
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .get('/', ({ cookie: { name } }) => {
+ // Get
+ name.value
+
+ // Set
+ name.value = "New Value"
+ })
+```
+
+Auto-encodes/decodes objects. Just works.
+
+## Reactivity
+Signal-like approach. Single source of truth. Auto-sets headers, syncs values.
+
+Cookie jar = Proxy object. Extract value always `Cookie`, never `undefined`. Access via `.value`.
+
+Iterate over cookie jar → only existing cookies.
+
+## Cookie Attributes
+
+### Direct Property Assignment
+```typescript
+.get('/', ({ cookie: { name } }) => {
+ // Get
+ name.domain
+
+ // Set
+ name.domain = 'millennium.sh'
+ name.httpOnly = true
+})
+```
+
+### set - Reset All Properties
+```typescript
+.get('/', ({ cookie: { name } }) => {
+ name.set({
+ domain: 'millennium.sh',
+ httpOnly: true
+ })
+})
+```
+
+Overwrites all properties.
+
+### add - Update Specific Properties
+Like `set` but only overwrites defined properties.
+
+## Remove Cookie
+```typescript
+.get('/', ({ cookie, cookie: { name } }) => {
+ name.remove()
+ // or
+ delete cookie.name
+})
+```
+
+## Cookie Schema
+Strict validation + type inference with `t.Cookie`:
+```typescript
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .get('/', ({ cookie: { name } }) => {
+ name.value = {
+ id: 617,
+ name: 'Summoning 101'
+ }
+ }, {
+ cookie: t.Cookie({
+ name: t.Object({
+ id: t.Numeric(),
+ name: t.String()
+ })
+ })
+ })
+```
+
+### Nullable Cookie
+```typescript
+cookie: t.Cookie({
+ name: t.Optional(
+ t.Object({
+ id: t.Numeric(),
+ name: t.String()
+ })
+ )
+})
+```
+
+## Cookie Signature
+Cryptographic hash for verification. Prevents malicious modification.
+
+```typescript
+new Elysia()
+ .get('/', ({ cookie: { profile } }) => {
+ profile.value = { id: 617, name: 'Summoning 101' }
+ }, {
+ cookie: t.Cookie({
+ profile: t.Object({
+ id: t.Numeric(),
+ name: t.String()
+ })
+ }, {
+ secrets: 'Fischl von Luftschloss Narfidort',
+ sign: ['profile']
+ })
+ })
+```
+
+Auto-signs/unsigns.
+
+### Global Config
+```typescript
+new Elysia({
+ cookie: {
+ secrets: 'Fischl von Luftschloss Narfidort',
+ sign: ['profile']
+ }
+})
+```
+
+## Cookie Rotation
+Auto-handles secret rotation. Old signature verification + new signature signing.
+
+```typescript
+new Elysia({
+ cookie: {
+ secrets: ['Vengeance will be mine', 'Fischl von Luftschloss Narfidort']
+ }
+})
+```
+
+Array = key rotation (retire old, replace with new).
+
+## Config
+
+### secrets
+Secret key for signing/unsigning. Array = key rotation.
+
+### domain
+Domain Set-Cookie attribute. Default: none (current domain only).
+
+### encode
+Function to encode value. Default: `encodeURIComponent`.
+
+### expires
+Date for Expires attribute. Default: none (non-persistent, deleted on browser exit).
+
+If both `expires` and `maxAge` set, `maxAge` takes precedence (spec-compliant clients).
+
+### httpOnly (false)
+HttpOnly attribute. If true, JS can't access via `document.cookie`.
+
+### maxAge (undefined)
+Seconds for Max-Age attribute. Rounded down to integer.
+
+If both `expires` and `maxAge` set, `maxAge` takes precedence (spec-compliant clients).
+
+### path
+Path attribute. Default: handler path.
+
+### priority
+Priority attribute: `low` | `medium` | `high`. Not fully standardized.
+
+### sameSite
+SameSite attribute:
+- `true` = Strict
+- `false` = not set
+- `'lax'` = Lax
+- `'none'` = None (explicit cross-site)
+- `'strict'` = Strict
+
+Not fully standardized.
+
+### secure
+Secure attribute. If true, only HTTPS. Clients won't send over HTTP.
diff --git a/.agent/skills/tech-stack/elysiajs/references/deployment.md b/.agent/skills/tech-stack/elysiajs/references/deployment.md
new file mode 100644
index 0000000..3c4cca8
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/deployment.md
@@ -0,0 +1,413 @@
+# Deployment
+
+## Production Build
+
+### Compile to Binary (Recommended)
+```bash
+bun build \
+ --compile \
+ --minify-whitespace \
+ --minify-syntax \
+ --target bun \
+ --outfile server \
+ src/index.ts
+```
+
+**Benefits:**
+- No runtime needed on deployment server
+- Smaller memory footprint (2-3x reduction)
+- Faster startup
+- Single portable executable
+
+**Run the binary:**
+```bash
+./server
+```
+
+### Compile to JavaScript
+```bash
+bun build \
+ --minify-whitespace \
+ --minify-syntax \
+ --outfile ./dist/index.js \
+ src/index.ts
+```
+
+**Run:**
+```bash
+NODE_ENV=production bun ./dist/index.js
+```
+
+## Docker
+
+### Basic Dockerfile
+```dockerfile
+FROM oven/bun:1 AS build
+
+WORKDIR /app
+
+# Cache dependencies
+COPY package.json bun.lock ./
+RUN bun install
+
+COPY ./src ./src
+
+ENV NODE_ENV=production
+
+RUN bun build \
+ --compile \
+ --minify-whitespace \
+ --minify-syntax \
+ --outfile server \
+ src/index.ts
+
+FROM gcr.io/distroless/base
+
+WORKDIR /app
+
+COPY --from=build /app/server server
+
+ENV NODE_ENV=production
+
+CMD ["./server"]
+
+EXPOSE 3000
+```
+
+### Build and Run
+```bash
+docker build -t my-elysia-app .
+docker run -p 3000:3000 my-elysia-app
+```
+
+### With Environment Variables
+```dockerfile
+FROM gcr.io/distroless/base
+
+WORKDIR /app
+
+COPY --from=build /app/server server
+
+ENV NODE_ENV=production
+ENV PORT=3000
+ENV DATABASE_URL=""
+ENV JWT_SECRET=""
+
+CMD ["./server"]
+
+EXPOSE 3000
+```
+
+## Cluster Mode (Multiple CPU Cores)
+
+```typescript
+// src/index.ts
+import cluster from 'node:cluster'
+import os from 'node:os'
+import process from 'node:process'
+
+if (cluster.isPrimary) {
+ for (let i = 0; i < os.availableParallelism(); i++) {
+ cluster.fork()
+ }
+} else {
+ await import('./server')
+ console.log(`Worker ${process.pid} started`)
+}
+```
+
+```typescript
+// src/server.ts
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .get('/', () => 'Hello World!')
+ .listen(3000)
+```
+
+## Environment Variables
+
+### .env File
+```env
+NODE_ENV=production
+PORT=3000
+DATABASE_URL=postgresql://user:password@localhost:5432/db
+JWT_SECRET=your-secret-key
+CORS_ORIGIN=https://example.com
+```
+
+### Load in App
+```typescript
+import { Elysia } from 'elysia'
+
+const app = new Elysia()
+ .get('/env', () => ({
+ env: process.env.NODE_ENV,
+ port: process.env.PORT
+ }))
+ .listen(parseInt(process.env.PORT || '3000'))
+```
+
+## Platform-Specific Deployments
+
+### Railway
+```typescript
+// Railway assigns random PORT via env variable
+new Elysia()
+ .get('/', () => 'Hello Railway')
+ .listen(process.env.PORT ?? 3000)
+```
+
+### Vercel
+```typescript
+// src/index.ts
+import { Elysia } from 'elysia'
+
+export default new Elysia()
+ .get('/', () => 'Hello Vercel')
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+```json
+// vercel.json
+{
+ "$schema": "https://openapi.vercel.sh/vercel.json",
+ "bunVersion": "1.x"
+}
+```
+
+### Cloudflare Workers
+```typescript
+import { Elysia } from 'elysia'
+import { CloudflareAdapter } from 'elysia/adapter/cloudflare-worker'
+
+export default new Elysia({
+ adapter: CloudflareAdapter
+})
+ .get('/', () => 'Hello Cloudflare!')
+ .compile()
+```
+
+```toml
+# wrangler.toml
+name = "elysia-app"
+main = "src/index.ts"
+compatibility_date = "2025-06-01"
+```
+
+### Node.js Adapter
+```typescript
+import { Elysia } from 'elysia'
+import { node } from '@elysiajs/node'
+
+const app = new Elysia({ adapter: node() })
+ .get('/', () => 'Hello Node.js')
+ .listen(3000)
+```
+
+## Performance Optimization
+
+### Enable AoT Compilation
+```typescript
+new Elysia({
+ aot: true // Ahead-of-time compilation
+})
+```
+
+### Use Native Static Response
+```typescript
+new Elysia({
+ nativeStaticResponse: true
+})
+ .get('/version', 1) // Optimized for Bun.serve.static
+```
+
+### Precompile Routes
+```typescript
+new Elysia({
+ precompile: true // Compile all routes ahead of time
+})
+```
+
+## Health Checks
+
+```typescript
+new Elysia()
+ .get('/health', () => ({
+ status: 'ok',
+ timestamp: Date.now()
+ }))
+ .get('/ready', ({ db }) => {
+ // Check database connection
+ const isDbReady = checkDbConnection()
+
+ if (!isDbReady) {
+ return status(503, { status: 'not ready' })
+ }
+
+ return { status: 'ready' }
+ })
+```
+
+## Graceful Shutdown
+
+```typescript
+import { Elysia } from 'elysia'
+
+const app = new Elysia()
+ .get('/', () => 'Hello')
+ .listen(3000)
+
+process.on('SIGTERM', () => {
+ console.log('SIGTERM received, shutting down gracefully')
+ app.stop()
+ process.exit(0)
+})
+
+process.on('SIGINT', () => {
+ console.log('SIGINT received, shutting down gracefully')
+ app.stop()
+ process.exit(0)
+})
+```
+
+## Monitoring
+
+### OpenTelemetry
+```typescript
+import { opentelemetry } from '@elysiajs/opentelemetry'
+
+new Elysia()
+ .use(opentelemetry({
+ serviceName: 'my-service',
+ endpoint: 'http://localhost:4318'
+ }))
+```
+
+### Custom Logging
+```typescript
+.onRequest(({ request }) => {
+ console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`)
+})
+.onAfterResponse(({ request, set }) => {
+ console.log(`[${new Date().toISOString()}] ${request.method} ${request.url} - ${set.status}`)
+})
+```
+
+## SSL/TLS (HTTPS)
+
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia({
+ serve: {
+ tls: {
+ cert: file('cert.pem'),
+ key: file('key.pem')
+ }
+ }
+})
+ .get('/', () => 'Hello HTTPS')
+ .listen(3000)
+```
+
+## Best Practices
+
+1. **Always compile to binary for production**
+ - Reduces memory usage
+ - Smaller deployment size
+ - No runtime needed
+
+2. **Use environment variables**
+ - Never hardcode secrets
+ - Use different configs per environment
+
+3. **Enable health checks**
+ - Essential for load balancers
+ - K8s/Docker orchestration
+
+4. **Implement graceful shutdown**
+ - Handle SIGTERM/SIGINT
+ - Close connections properly
+
+5. **Use cluster mode**
+ - Utilize all CPU cores
+ - Better performance under load
+
+6. **Monitor your app**
+ - Use OpenTelemetry
+ - Log requests/responses
+ - Track errors
+
+## Example Production Setup
+
+```typescript
+// src/server.ts
+import { Elysia } from 'elysia'
+import { cors } from '@elysiajs/cors'
+import { opentelemetry } from '@elysiajs/opentelemetry'
+
+export const app = new Elysia({
+ aot: true,
+ nativeStaticResponse: true
+})
+ .use(cors({
+ origin: process.env.CORS_ORIGIN || 'http://localhost:3000'
+ }))
+ .use(opentelemetry({
+ serviceName: 'my-service'
+ }))
+ .get('/health', () => ({ status: 'ok' }))
+ .get('/', () => 'Hello Production')
+ .listen(parseInt(process.env.PORT || '3000'))
+
+// Graceful shutdown
+process.on('SIGTERM', () => {
+ app.stop()
+ process.exit(0)
+})
+```
+
+```typescript
+// src/index.ts (cluster)
+import cluster from 'node:cluster'
+import os from 'node:os'
+
+if (cluster.isPrimary) {
+ for (let i = 0; i < os.availableParallelism(); i++) {
+ cluster.fork()
+ }
+} else {
+ await import('./server')
+}
+```
+
+```dockerfile
+# Dockerfile
+FROM oven/bun:1 AS build
+
+WORKDIR /app
+
+COPY package.json bun.lock ./
+RUN bun install
+
+COPY ./src ./src
+
+ENV NODE_ENV=production
+
+RUN bun build --compile --outfile server src/index.ts
+
+FROM gcr.io/distroless/base
+
+WORKDIR /app
+
+COPY --from=build /app/server server
+
+ENV NODE_ENV=production
+
+CMD ["./server"]
+
+EXPOSE 3000
+```
diff --git a/.agent/skills/tech-stack/elysiajs/references/eden.md b/.agent/skills/tech-stack/elysiajs/references/eden.md
new file mode 100644
index 0000000..7d9165d
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/eden.md
@@ -0,0 +1,158 @@
+# Eden Treaty
+e2e type safe RPC client for share type from backend to frontend.
+
+## What It Is
+Type-safe object representation for Elysia server. Auto-completion + error handling.
+
+## Installation
+```bash
+bun add @elysiajs/eden
+bun add -d elysia
+```
+
+Export Elysia server type:
+```typescript
+const app = new Elysia()
+ .get('/', () => 'Hi Elysia')
+ .get('/id/:id', ({ params: { id } }) => id)
+ .post('/mirror', ({ body }) => body, {
+ body: t.Object({
+ id: t.Number(),
+ name: t.String()
+ })
+ })
+ .listen(3000)
+
+export type App = typeof app
+```
+
+Consume on client side:
+```typescript
+import { treaty } from '@elysiajs/eden'
+import type { App } from './server'
+
+const client = treaty('localhost:3000')
+
+// response: Hi Elysia
+const { data: index } = await client.get()
+
+// response: 1895
+const { data: id } = await client.id({ id: 1895 }).get()
+
+// response: { id: 1895, name: 'Skadi' }
+const { data: nendoroid } = await client.mirror.post({
+ id: 1895,
+ name: 'Skadi'
+})
+```
+
+## Common Errors & Fixes
+- **Strict mode**: Enable in tsconfig
+- **Version mismatch**: `npm why elysia` - must match server/client
+- **TypeScript**: Min 5.0
+- **Method chaining**: Required on server
+- **Bun types**: `bun add -d @types/bun` if using Bun APIs
+- **Path alias**: Must resolve same on frontend/backend
+
+### Monorepo Path Alias
+Must resolve to same file on frontend/backend
+
+```json
+// tsconfig.json at root
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@frontend/*": ["./apps/frontend/src/*"],
+ "@backend/*": ["./apps/backend/src/*"]
+ }
+ }
+}
+```
+
+## Syntax Mapping
+| Path | Method | Treaty |
+|----------------|--------|-------------------------------|
+| / | GET | `.get()` |
+| /hi | GET | `.hi.get()` |
+| /deep/nested | POST | `.deep.nested.post()` |
+| /item/:name | GET | `.item({ name: 'x' }).get()` |
+
+## Parameters
+
+### With body (POST/PUT/PATCH/DELETE):
+```typescript
+.user.post(
+ { name: 'Elysia' }, // body
+ { headers: {}, query: {}, fetch: {} } // optional
+)
+```
+
+### No body (GET/HEAD):
+```typescript
+.hello.get({ headers: {}, query: {}, fetch: {} })
+```
+
+### Empty body with query/headers:
+```typescript
+.user.post(null, { query: { name: 'Ely' } })
+```
+
+### Fetch options:
+```typescript
+.hello.get({ fetch: { signal: controller.signal } })
+```
+
+### File upload:
+```typescript
+// Accepts: File | File[] | FileList | Blob
+.image.post({
+ title: 'Title',
+ image: fileInput.files!
+})
+```
+
+## Response
+```typescript
+const { data, error, response, status, headers } = await api.user.post({ name: 'x' })
+
+if (error) {
+ switch (error.status) {
+ case 400: throw error.value
+ default: throw error.value
+ }
+}
+// data unwrapped after error handling
+return data
+```
+
+status >= 300 → `data = null`, `error` has value
+
+## Stream/SSE
+Interpreted as `AsyncGenerator`:
+```typescript
+const { data, error } = await treaty(app).ok.get()
+if (error) throw error
+
+for await (const chunk of data) console.log(chunk)
+```
+
+## Utility Types
+```typescript
+import { Treaty } from '@elysiajs/eden'
+
+type UserData = Treaty.Data
+type UserError = Treaty.Error
+```
+
+## WebSocket
+```typescript
+const chat = api.chat.subscribe()
+
+chat.subscribe((message) => console.log('got', message))
+chat.on('open', () => chat.send('hello'))
+
+// Native access: chat.raw
+```
+
+`.subscribe()` accepts same params as `get`/`head`
diff --git a/.agent/skills/tech-stack/elysiajs/references/lifecycle.md b/.agent/skills/tech-stack/elysiajs/references/lifecycle.md
new file mode 100644
index 0000000..645584e
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/lifecycle.md
@@ -0,0 +1,198 @@
+# Lifecycle
+
+Instead of a sequential process, Elysia's request handling is divided into multiple stages called lifecycle events.
+
+It's designed to separate the process into distinct phases based on their responsibility without interfering with each others.
+
+### List of events in order
+
+1. **request** - early, global
+2. **parse** - body parsing
+3. **transform** / **derive** - mutate context pre validation
+4. **beforeHandle** / **resolve** - auth/guard logic
+5. **handler** - your business code
+6. **afterHandle** - tweak response, set headers
+7. **mapResponse** - turn anything into a proper `Response`
+8. **onError** - centralized error handling
+9. **onAfterResponse** - post response/cleanup tasks
+
+## Request (`onRequest`)
+
+Runs first for every incoming request.
+
+- Ideal for **caching, rate limiting, CORS, adding global headers**.
+- If the hook returns a value, the whole lifecycle stops and that value becomes the response.
+
+```ts
+new Elysia().onRequest(({ ip, set }) => {
+ if (blocked(ip)) return (set.status = 429)
+})
+```
+
+---
+
+## Parse (`onParse`)
+
+_Body parsing stage._
+
+- Handles `text/plain`, `application/json`, `multipart/form-data`, `application/x www-form-urlencoded` by default.
+- Use to add **custom parsers** or support extra `Content Type`s.
+
+```ts
+new Elysia().onParse(({ request, contentType }) => {
+ if (contentType === 'application/custom') return request.text()
+})
+```
+
+---
+
+## Transform (`onTransform`)
+
+_Runs **just before validation**; can mutate the request context._
+
+- Perfect for **type coercion**, trimming strings, or adding temporary fields that validation will use.
+
+```ts
+new Elysia().onTransform(({ params }) => {
+ params.id = Number(params.id)
+})
+```
+
+---
+
+## Derive
+
+_Runs along with `onTransform` **but before validation**; adds per request values to the context._
+
+- Useful for extracting info from headers, cookies, query, etc., that you want to reuse in handlers.
+
+```ts
+new Elysia().derive(({ headers }) => ({
+ bearer: headers.authorization?.replace(/^Bearer /, '')
+}))
+```
+
+---
+
+## Before Handle (`onBeforeHandle`)
+
+_Executed after validation, right before the route handler._
+
+- Great for **auth checks, permission gating, custom pre validation logic**.
+- Returning a value skips the handler.
+
+```ts
+new Elysia().get('/', () => 'hi', {
+ beforeHandle({ cookie, status }) {
+ if (!cookie.session) return status(401)
+ }
+})
+```
+
+---
+
+## Resolve
+
+_Like `derive` but runs **after validation** along "Before Handle" (so you can rely on validated data)._
+
+- Usually placed inside a `guard` because it isn't available as a local hook.
+
+```ts
+new Elysia().guard(
+ { headers: t.Object({ authorization: t.String() }) },
+ (app) =>
+ app
+ .resolve(({ headers }) => ({
+ bearer: headers.authorization.split(' ')[1]
+ }))
+ .get('/', ({ bearer }) => bearer)
+)
+```
+
+---
+
+## After Handle (`onAfterHandle`)
+
+_Runs after the handler finishes._
+
+- Can **modify response headers**, wrap the result in a `Response`, or transform the payload.
+- Returning a value **replaces** the handler’s result, but the next `afterHandle` hooks still run.
+
+```ts
+new Elysia().get('/', () => '
Hello
', {
+ afterHandle({ response, set }) {
+ if (isHtml(response)) {
+ set.headers['content-type'] = 'text/html; charset=utf-8'
+ return new Response(response)
+ }
+ }
+})
+```
+
+---
+
+## Map Response (`mapResponse`)
+
+_Runs right after all `afterHandle` hooks; maps **any** value to a Web standard `Response`._
+
+- Ideal for **compression, custom content type mapping, streaming**.
+
+```ts
+new Elysia().mapResponse(({ responseValue, set }) => {
+ const body =
+ typeof responseValue === 'object'
+ ? JSON.stringify(responseValue)
+ : String(responseValue ?? '')
+
+ set.headers['content-encoding'] = 'gzip'
+ return new Response(Bun.gzipSync(new TextEncoder().encode(body)), {
+ headers: {
+ 'Content-Type':
+ typeof responseValue === 'object'
+ ? 'application/json'
+ : 'text/plain'
+ }
+ })
+})
+```
+
+---
+
+## On Error (`onError`)
+
+_Caught whenever an error bubbles up from any lifecycle stage._
+
+- Use to **customize error messages**, **handle 404**, **log**, or **retry**.
+- Must be registered **before** the routes it should protect.
+
+```ts
+new Elysia().onError(({ code, status }) => {
+ if (code === 'NOT_FOUND') return status(404, 'â“ Not found')
+ return new Response('Oops', { status: 500 })
+})
+```
+
+---
+
+## After Response (`onAfterResponse`)
+
+_Runs **after** the response has been sent to the client._
+
+- Perfect for **logging, metrics, cleanup**.
+
+```ts
+new Elysia().onAfterResponse(() =>
+ console.log('✅ response sent at', Date.now())
+)
+```
+
+---
+
+## Hook Types
+
+| Type | Scope | How to add |
+| -------------------- | --------------------------------- | --------------------------------------------------------- |
+| **Local Hook** | Single route | Inside route options (`afterHandle`, `beforeHandle`, …) |
+| **Interceptor Hook** | Whole instance (and later routes) | `.onXxx(cb)` or `.use(plugin)` |
+
+> **Remember:** Hooks only affect routes **defined after** they are registered, except `onRequest` which is global because it runs before route matching.
diff --git a/.agent/skills/tech-stack/elysiajs/references/macro.md b/.agent/skills/tech-stack/elysiajs/references/macro.md
new file mode 100644
index 0000000..f89ee75
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/macro.md
@@ -0,0 +1,83 @@
+# Macro
+
+Composable Elysia function for controlling lifecycle/schema/context with full type safety. Available in hook after definition control by key-value label.
+
+## Basic Pattern
+```typescript
+.macro({
+ hi: (word: string) => ({
+ beforeHandle() { console.log(word) }
+ })
+})
+.get('/', () => 'hi', { hi: 'Elysia' })
+```
+
+## Property Shorthand
+Object → function accepting boolean:
+```typescript
+.macro({
+ // These equivalent:
+ isAuth: { resolve: () => ({ user: 'saltyaom' }) },
+ isAuth(enabled: boolean) { if(enabled) return { resolve() {...} } }
+})
+```
+
+## Error Handling
+Return `status`, don't throw:
+```typescript
+.macro({
+ auth: {
+ resolve({ headers }) {
+ if(!headers.authorization) return status(401, 'Unauthorized')
+ return { user: 'SaltyAom' }
+ }
+ }
+})
+```
+
+## Resolve - Add Context Props
+```typescript
+.macro({
+ user: (enabled: true) => ({
+ resolve: () => ({ user: 'Pardofelis' })
+ })
+})
+.get('/', ({ user }) => user, { user: true })
+```
+
+### Named Macro for Type Inference
+TypeScript limitation workaround:
+```typescript
+.macro('user', { resolve: () => ({ user: 'lilith' }) })
+.macro('user2', { user: true, resolve: ({ user }) => {} })
+```
+
+## Schema
+Auto-validates, infers types, stacks with other schemas:
+```typescript
+.macro({
+ withFriends: {
+ body: t.Object({ friends: t.Tuple([...]) })
+ }
+})
+```
+
+Use named single macro for lifecycle type inference within same macro.
+
+## Extension
+Stack macros:
+```typescript
+.macro({
+ sartre: { body: t.Object({...}) },
+ fouco: { body: t.Object({...}) },
+ lilith: { fouco: true, sartre: true, body: t.Object({...}) }
+})
+```
+
+## Deduplication
+Auto-dedupes by property value. Custom seed:
+```typescript
+.macro({ sartre: (role: string) => ({ seed: role, ... }) })
+```
+
+Max stack: 16 (prevents infinite loops)
diff --git a/.agent/skills/tech-stack/elysiajs/references/plugin.md b/.agent/skills/tech-stack/elysiajs/references/plugin.md
new file mode 100644
index 0000000..cd10e64
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/plugin.md
@@ -0,0 +1,207 @@
+# Plugins
+
+## Plugin = Decoupled Elysia Instance
+
+```ts
+const plugin = new Elysia()
+ .decorate('plugin', 'hi')
+ .get('/plugin', ({ plugin }) => plugin)
+
+const app = new Elysia()
+ .use(plugin) // inherit properties
+ .get('/', ({ plugin }) => plugin)
+```
+
+**Inherits**: state, decorate
+**Does NOT inherit**: lifecycle (isolated by default)
+
+## Dependency
+
+Each instance runs independently like microservice. **Must explicitly declare dependencies**.
+
+```ts
+const auth = new Elysia()
+ .decorate('Auth', Auth)
+
+// ❌ Missing dependency
+const main = new Elysia()
+ .get('/', ({ Auth }) => Auth.getProfile())
+
+// ✅ Declare dependency
+const main = new Elysia()
+ .use(auth) // required for Auth
+ .get('/', ({ Auth }) => Auth.getProfile())
+```
+
+## Deduplication
+
+**Every plugin re-executes by default**. Use `name` + optional `seed` to deduplicate:
+
+```ts
+const ip = new Elysia({ name: 'ip' }) // unique identifier
+ .derive({ as: 'global' }, ({ server, request }) => ({
+ ip: server?.requestIP(request)
+ }))
+
+const router1 = new Elysia().use(ip)
+const router2 = new Elysia().use(ip)
+const server = new Elysia().use(router1).use(router2)
+// `ip` only executes once due to deduplication
+```
+
+## Global vs Explicit Dependency
+
+**Global plugin** (rare, apply everywhere):
+- Doesn't add types - cors, compress, helmet
+- Global lifecycle no instance controls - tracing, logging
+- Examples: OpenAPI docs, OpenTelemetry, logging
+
+**Explicit dependency** (default, recommended):
+- Adds types - macro, state, model
+- Business logic instances interact with - Auth, DB
+- Examples: state management, ORM, auth, features
+
+## Scope
+
+**Lifecycle isolated by default**. Must specify scope to export.
+
+```ts
+// ❌ NOT inherited by app
+const profile = new Elysia()
+ .onBeforeHandle(({ cookie }) => throwIfNotSignIn(cookie))
+ .get('/profile', () => 'Hi')
+
+const app = new Elysia()
+ .use(profile)
+ .patch('/rename', ({ body }) => updateProfile(body)) // No sign-in check
+
+// ✅ Exported to app
+const profile = new Elysia()
+ .onBeforeHandle({ as: 'global' }, ({ cookie }) => throwIfNotSignIn(cookie))
+ .get('/profile', () => 'Hi')
+```
+
+## Scope Levels
+
+1. **local** (default) - current + descendants only
+2. **scoped** - parent + current + descendants
+3. **global** - all instances (all parents, current, descendants)
+
+Example with `.onBeforeHandle({ as: 'local' }, ...)`:
+
+| type | child | current | parent | main |
+|------|-------|---------|--------|------|
+| local | ✅ | ✅ | ❌ | ❌ |
+| scoped | ✅ | ✅ | ✅ | ❌ |
+| global | ✅ | ✅ | ✅ | ✅ |
+
+## Config
+
+```ts
+// Instance factory with config
+const version = (v = 1) => new Elysia()
+ .get('/version', v)
+
+const app = new Elysia()
+ .use(version(1))
+```
+
+## Functional Callback (not recommended)
+
+```ts
+// Harder to handle scope/encapsulation
+const plugin = (app: Elysia) => app
+ .state('counter', 0)
+ .get('/plugin', () => 'Hi')
+
+// Prefer new instance (better type inference, no perf diff)
+```
+
+## Guard (Apply to Multiple Routes)
+
+```ts
+.guard(
+ { body: t.Object({ username: t.String(), password: t.String() }) },
+ (app) =>
+ app.post('/sign-up', ({ body }) => signUp(body))
+ .post('/sign-in', ({ body }) => signIn(body))
+)
+```
+
+**Grouped guard** (merge group + guard):
+
+```ts
+.group(
+ '/v1',
+ { body: t.Literal('Rikuhachima Aru') }, // guard here
+ (app) => app.post('/student', ({ body }) => body)
+)
+```
+
+## Scope Casting
+
+**3 methods to apply hook to parent**:
+
+1. **Inline as** (single hook):
+```ts
+.derive({ as: 'scoped' }, () => ({ hi: 'ok' }))
+```
+
+2. **Guard as** (multiple hooks, no derive/resolve):
+```ts
+.guard({
+ as: 'scoped',
+ response: t.String(),
+ beforeHandle() { console.log('ok') }
+})
+```
+
+3. **Instance as** (all hooks + schema):
+```ts
+const plugin = new Elysia()
+ .derive(() => ({ hi: 'ok' }))
+ .get('/child', ({ hi }) => hi)
+ .as('scoped') // lift scope up
+```
+
+`.as()` lifts scope: local → scoped → global
+
+## Lazy Load
+
+**Deferred module** (async plugin, non-blocking startup):
+
+```ts
+// plugin.ts
+export const loadStatic = async (app: Elysia) => {
+ const files = await loadAllFiles()
+ files.forEach((asset) => app.get(asset, file(asset)))
+ return app
+}
+
+// main.ts
+const app = new Elysia().use(loadStatic)
+```
+
+**Lazy-load module** (dynamic import):
+
+```ts
+const app = new Elysia()
+ .use(import('./plugin')) // loaded after startup
+```
+
+**Testing** (wait for modules):
+
+```ts
+await app.modules // ensure all deferred/lazy modules loaded
+```
+
+## Notes
+[Inference] Based on docs patterns:
+- Use inline values for static resources (performance optimization)
+- Group routes by prefix for organization
+- Extend context minimally (separation of concerns)
+- Use `status()` over `set.status` for type safety
+- Prefer `resolve()` over `derive()` when type integrity matters
+- Plugins isolated by default (must declare scope explicitly)
+- Use `name` for deduplication when plugin used multiple times
+- Prefer explicit dependency over global (better modularity/tracking)
diff --git a/.agent/skills/tech-stack/elysiajs/references/route.md b/.agent/skills/tech-stack/elysiajs/references/route.md
new file mode 100644
index 0000000..c767283
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/route.md
@@ -0,0 +1,331 @@
+# ElysiaJS: Routing, Handlers & Context
+
+## Routing
+
+### Path Types
+
+```ts
+new Elysia()
+ .get('/static', 'static path') // exact match
+ .get('/id/:id', 'dynamic path') // captures segment
+ .get('/id/*', 'wildcard path') // captures rest
+```
+
+**Path Priority**: static > dynamic > wildcard
+
+### Dynamic Paths
+
+```ts
+new Elysia()
+ .get('/id/:id', ({ params: { id } }) => id)
+ .get('/id/:id/:name', ({ params: { id, name } }) => id + ' ' + name)
+```
+
+**Optional params**: `.get('/id/:id?', ...)`
+
+### HTTP Verbs
+
+- `.get()` - retrieve data
+- `.post()` - submit/create
+- `.put()` - replace
+- `.patch()` - partial update
+- `.delete()` - remove
+- `.all()` - any method
+- `.route(method, path, handler)` - custom verb
+
+### Grouping Routes
+
+```ts
+new Elysia()
+ .group('/user', { body: t.Literal('auth') }, (app) =>
+ app.post('/sign-in', ...)
+ .post('/sign-up', ...)
+)
+
+// Or use prefix in constructor
+new Elysia({ prefix: '/user' })
+ .post('/sign-in', ...)
+```
+
+## Handlers
+
+### Handler = function accepting HTTP request, returning response
+
+```ts
+// Inline value (compiled ahead, optimized)
+.get('/', 'Hello Elysia')
+.get('/video', file('video.mp4'))
+
+// Function handler
+.get('/', () => 'hello')
+.get('/', ({ params, query, body }) => {...})
+```
+
+### Context Properties
+
+- `body` - HTTP message/form/file
+- `query` - query string as object
+- `params` - path parameters
+- `headers` - HTTP headers
+- `cookie` - mutable signal for cookies
+- `store` - global mutable state
+- `request` - Web Standard Request
+- `server` - Bun server instance
+- `path` - request pathname
+
+### Context Utilities
+
+```ts
+import { redirect, form } from 'elysia'
+
+new Elysia().get('/', ({ status, set, form }) => {
+ // Status code (type-safe)
+ status(418, "I'm a teapot")
+
+ // Set response props
+ set.headers['x-custom'] = 'value'
+ set.status = 418 // legacy, no type inference
+
+ // Redirect
+ return redirect('https://...', 302)
+
+ // Cookies (mutable signal, no get/set)
+ cookie.name.value // get
+ cookie.name.value = 'new' // set
+
+ // FormData response
+ return form({ name: 'Party', images: [file('a.jpg')] })
+
+ // Single file
+ return file('document.pdf')
+})
+```
+
+### Streaming
+
+```ts
+new Elysia()
+ .get('/stream', function* () {
+ yield 1
+ yield 2
+ yield 3
+ })
+ // Server-Sent Events
+ .get('/sse', function* () {
+ yield sse('hello')
+ yield sse({ event: 'msg', data: {...} })
+ })
+```
+
+**Note**: Headers only settable before first yield
+
+**Conditional stream**: returning without yield converts to normal response
+
+## Context Extension
+
+[Inference] Extend when property is:
+
+- Global mutable (use `state`)
+- Request/response related (use `decorate`)
+- Derived from existing props (use `derive`/`resolve`)
+
+### state() - Global Mutable
+
+```ts
+new Elysia()
+ `.state('version', 1)
+ .get('/', ({ store: { version } }) => version)
+ // Multiple
+ .state({ counter: 0, visits: 0 })
+
+ // Remap (create new from existing)
+ .state(({ version, ...store }) => ({
+ ...store,
+ apiVersion: version
+ }))
+````
+
+**Gotcha**: Use reference not value
+
+```ts
+new Elysia()
+ // ✅ Correct
+ .get('/', ({ store }) => store.counter++)
+
+ // ❌ Wrong - loses reference
+ .get('/', ({ store: { counter } }) => counter++)
+```
+
+### decorate() - Additional Context Props
+
+```ts
+new Elysia()
+ .decorate('logger', new Logger())
+ .get('/', ({ logger }) => logger.log('hi'))
+
+ // Multiple
+ .decorate({ logger: new Logger(), db: connection })
+```
+
+**When**: constant/readonly values, classes with internal state, singletons
+
+### derive() - Create from Existing (Transform Lifecycle)
+
+```ts
+new Elysia()
+ .derive(({ headers }) => ({
+ bearer: headers.authorization?.startsWith('Bearer ')
+ ? headers.authorization.slice(7)
+ : null
+ }))
+ .get('/', ({ bearer }) => bearer)
+```
+
+**Timing**: runs at transform (before validation)
+**Type safety**: request props typed as `unknown`
+
+### resolve() - Type-Safe Derive (beforeHandle Lifecycle)
+
+```ts
+new Elysia()
+ .guard({
+ headers: t.Object({
+ bearer: t.String({ pattern: '^Bearer .+$' })
+ })
+ })
+ .resolve(({ headers }) => ({
+ bearer: headers.bearer.slice(7) // typed correctly
+ }))
+```
+
+**Timing**: runs at beforeHandle (after validation)
+**Type safety**: request props fully typed
+
+### Error from derive/resolve
+
+```ts
+new Elysia()
+ .derive(({ headers, status }) => {
+ if (!headers.authorization) return status(400)
+ return { bearer: ... }
+ })
+```
+
+Returns early if error returned
+
+## Patterns
+
+### Affix (Bulk Remap)
+
+```ts
+const plugin = new Elysia({ name: 'setup' }).decorate({
+ argon: 'a',
+ boron: 'b'
+})
+
+new Elysia()
+ .use(plugin)
+ .prefix('decorator', 'setup') // setupArgon, setupBoron
+ .prefix('all', 'setup') // remap everything
+```
+
+### Assignment Patterns
+
+1. **key-value**: `.state('key', value)`
+2. **object**: `.state({ k1: v1, k2: v2 })`
+3. **remap**: `.state(({old}) => ({new}))`
+
+## Testing
+
+```ts
+const app = new Elysia().get('/', 'hi')
+
+// Programmatic test
+app.handle(new Request('http://localhost/'))
+```
+
+## To Throw or Return
+
+Most of an error handling in Elysia can be done by throwing an error and will be handle in `onError`.
+
+But for `status` it can be a little bit confusing, since it can be used both as a return value or throw an error.
+
+It could either be **return** or **throw** based on your specific needs.
+
+- If an `status` is **throw**, it will be caught by `onError` middleware.
+- If an `status` is **return**, it will be **NOT** caught by `onError` middleware.
+
+See the following code:
+
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia()
+ .onError(({ code, error, path }) => {
+ if (code === 418) return 'caught'
+ })
+ .get('/throw', ({ status }) => {
+ // This will be caught by onError
+ throw status(418)
+ })
+ .get('/return', ({ status }) => {
+ // This will NOT be caught by onError
+ return status(418)
+ })
+```
+
+## To Throw or Return
+
+Elysia provide a `status` function for returning HTTP status code, prefers over `set.status`.
+
+`status` can be import from Elysia but preferably extract from route handler Context for type safety.
+
+```ts
+import { Elysia, status } from 'elysia'
+
+function doThing() {
+ if (Math.random() > 0.33) return status(418, "I'm a teapot")
+}
+
+new Elysia().get('/', ({ status }) => {
+ if (Math.random() > 0.33) return status(418)
+
+ return 'ok'
+})
+```
+
+Error Handling in Elysia can be done by throwing an error and will be handle in `onError`.
+
+Status could either be **return** or **throw** based on your specific needs.
+
+- If an `status` is **throw**, it will be caught by `onError` middleware.
+- If an `status` is **return**, it will be **NOT** caught by `onError` middleware.
+
+See the following code:
+
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia()
+ .onError(({ code, error, path }) => {
+ if (code === 418) return 'caught'
+ })
+ .get('/throw', ({ status }) => {
+ // This will be caught by onError
+ throw status(418)
+ })
+ .get('/return', ({ status }) => {
+ // This will NOT be caught by onError
+ return status(418)
+ })
+```
+
+## Notes
+
+[Inference] Based on docs patterns:
+
+- Use inline values for static resources (performance optimization)
+- Group routes by prefix for organization
+- Extend context minimally (separation of concerns)
+- Use `status()` over `set.status` for type safety
+- Prefer `resolve()` over `derive()` when type integrity matters
diff --git a/.agent/skills/tech-stack/elysiajs/references/testing.md b/.agent/skills/tech-stack/elysiajs/references/testing.md
new file mode 100644
index 0000000..ffcdff3
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/testing.md
@@ -0,0 +1,385 @@
+# Unit Testing
+
+## Basic Test Setup
+
+### Installation
+```bash
+bun add -d @elysiajs/eden
+```
+
+### Basic Test
+```typescript
+// test/app.test.ts
+import { describe, expect, it } from 'bun:test'
+import { Elysia } from 'elysia'
+
+describe('Elysia App', () => {
+ it('should return hello world', async () => {
+ const app = new Elysia()
+ .get('/', () => 'Hello World')
+
+ const res = await app.handle(
+ new Request('http://localhost/')
+ )
+
+ expect(res.status).toBe(200)
+ expect(await res.text()).toBe('Hello World')
+ })
+})
+```
+
+## Testing Routes
+
+### GET Request
+```typescript
+it('should get user by id', async () => {
+ const app = new Elysia()
+ .get('/user/:id', ({ params: { id } }) => ({
+ id,
+ name: 'John Doe'
+ }))
+
+ const res = await app.handle(
+ new Request('http://localhost/user/123')
+ )
+
+ const data = await res.json()
+
+ expect(res.status).toBe(200)
+ expect(data).toEqual({
+ id: '123',
+ name: 'John Doe'
+ })
+})
+```
+
+### POST Request
+```typescript
+it('should create user', async () => {
+ const app = new Elysia()
+ .post('/user', ({ body }) => body)
+
+ const res = await app.handle(
+ new Request('http://localhost/user', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ name: 'Jane Doe',
+ email: 'jane@example.com'
+ })
+ })
+ )
+
+ const data = await res.json()
+
+ expect(res.status).toBe(200)
+ expect(data.name).toBe('Jane Doe')
+})
+```
+
+## Testing Module/Plugin
+
+### Module Structure
+```
+src/
+├── modules/
+│ └── auth/
+│ ├── index.ts # Elysia instance
+│ ├── service.ts
+│ └── model.ts
+└── index.ts
+```
+
+### Auth Module
+```typescript
+// src/modules/auth/index.ts
+import { Elysia, t } from 'elysia'
+
+export const authModule = new Elysia({ prefix: '/auth' })
+ .post('/login', ({ body, cookie: { session } }) => {
+ if (body.username === 'admin' && body.password === 'password') {
+ session.value = 'valid-session'
+ return { success: true }
+ }
+ return { success: false }
+ }, {
+ body: t.Object({
+ username: t.String(),
+ password: t.String()
+ })
+ })
+ .get('/profile', ({ cookie: { session }, status }) => {
+ if (!session.value) {
+ return status(401, { error: 'Unauthorized' })
+ }
+ return { username: 'admin' }
+ })
+```
+
+### Auth Module Test
+```typescript
+// test/auth.test.ts
+import { describe, expect, it } from 'bun:test'
+import { authModule } from '../src/modules/auth'
+
+describe('Auth Module', () => {
+ it('should login successfully', async () => {
+ const res = await authModule.handle(
+ new Request('http://localhost/auth/login', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ username: 'admin',
+ password: 'password'
+ })
+ })
+ )
+
+ const data = await res.json()
+ expect(res.status).toBe(200)
+ expect(data.success).toBe(true)
+ })
+
+ it('should reject invalid credentials', async () => {
+ const res = await authModule.handle(
+ new Request('http://localhost/auth/login', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ username: 'wrong',
+ password: 'wrong'
+ })
+ })
+ )
+
+ const data = await res.json()
+ expect(data.success).toBe(false)
+ })
+
+ it('should return 401 for unauthenticated profile request', async () => {
+ const res = await authModule.handle(
+ new Request('http://localhost/auth/profile')
+ )
+
+ expect(res.status).toBe(401)
+ })
+})
+```
+
+## Eden Treaty Testing
+
+### Setup
+```typescript
+import { treaty } from '@elysiajs/eden'
+import { app } from '../src/modules/auth'
+
+const api = treaty(app)
+```
+
+### Eden Tests
+```typescript
+describe('Auth Module with Eden', () => {
+ it('should login with Eden', async () => {
+ const { data, error } = await api.auth.login.post({
+ username: 'admin',
+ password: 'password'
+ })
+
+ expect(error).toBeNull()
+ expect(data?.success).toBe(true)
+ })
+
+ it('should get profile with Eden', async () => {
+ // First login
+ await api.auth.login.post({
+ username: 'admin',
+ password: 'password'
+ })
+
+ // Then get profile
+ const { data, error } = await api.auth.profile.get()
+
+ expect(error).toBeNull()
+ expect(data?.username).toBe('admin')
+ })
+})
+```
+
+## Mocking Dependencies
+
+### With Decorators
+```typescript
+// app.ts
+export const app = new Elysia()
+ .decorate('db', realDatabase)
+ .get('/users', ({ db }) => db.getUsers())
+
+// test
+import { app } from '../src/app'
+
+describe('App with mocked DB', () => {
+ it('should use mock database', async () => {
+ const mockDb = {
+ getUsers: () => [{ id: 1, name: 'Test User' }]
+ }
+
+ const testApp = app.decorate('db', mockDb)
+
+ const res = await testApp.handle(
+ new Request('http://localhost/users')
+ )
+
+ const data = await res.json()
+ expect(data).toEqual([{ id: 1, name: 'Test User' }])
+ })
+})
+```
+
+## Testing with Headers
+
+```typescript
+it('should require authorization', async () => {
+ const app = new Elysia()
+ .get('/protected', ({ headers, status }) => {
+ if (!headers.authorization) {
+ return status(401)
+ }
+ return { data: 'secret' }
+ })
+
+ const res = await app.handle(
+ new Request('http://localhost/protected', {
+ headers: {
+ 'Authorization': 'Bearer token123'
+ }
+ })
+ )
+
+ expect(res.status).toBe(200)
+})
+```
+
+## Testing Validation
+
+```typescript
+import { Elysia, t } from 'elysia'
+
+it('should validate request body', async () => {
+ const app = new Elysia()
+ .post('/user', ({ body }) => body, {
+ body: t.Object({
+ name: t.String(),
+ age: t.Number({ minimum: 0 })
+ })
+ })
+
+ // Valid request
+ const validRes = await app.handle(
+ new Request('http://localhost/user', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ name: 'John',
+ age: 25
+ })
+ })
+ )
+ expect(validRes.status).toBe(200)
+
+ // Invalid request (negative age)
+ const invalidRes = await app.handle(
+ new Request('http://localhost/user', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ name: 'John',
+ age: -5
+ })
+ })
+ )
+ expect(invalidRes.status).toBe(400)
+})
+```
+
+## Testing WebSocket
+
+```typescript
+it('should handle websocket connection', (done) => {
+ const app = new Elysia()
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send('Echo: ' + message)
+ }
+ })
+
+ const ws = new WebSocket('ws://localhost:3000/chat')
+
+ ws.onopen = () => {
+ ws.send('Hello')
+ }
+
+ ws.onmessage = (event) => {
+ expect(event.data).toBe('Echo: Hello')
+ ws.close()
+ done()
+ }
+})
+```
+
+## Complete Example
+
+```typescript
+// src/modules/auth/index.ts
+import { Elysia, t } from 'elysia'
+
+export const authModule = new Elysia({ prefix: '/auth' })
+ .post('/login', ({ body, cookie: { session } }) => {
+ if (body.username === 'admin' && body.password === 'password') {
+ session.value = 'valid-session'
+ return { success: true }
+ }
+ return { success: false }
+ }, {
+ body: t.Object({
+ username: t.String(),
+ password: t.String()
+ })
+ })
+ .get('/profile', ({ cookie: { session }, status }) => {
+ if (!session.value) {
+ return status(401)
+ }
+ return { username: 'admin' }
+ })
+
+// test/auth.test.ts
+import { describe, expect, it } from 'bun:test'
+import { treaty } from '@elysiajs/eden'
+import { authModule } from '../src/modules/auth'
+
+const api = treaty(authModule)
+
+describe('Auth Module', () => {
+ it('should login successfully', async () => {
+ const { data, error } = await api.auth.login.post({
+ username: 'admin',
+ password: 'password'
+ })
+
+ expect(error).toBeNull()
+ expect(data?.success).toBe(true)
+ })
+
+ it('should return 401 for unauthorized access', async () => {
+ const { error } = await api.auth.profile.get()
+
+ expect(error?.status).toBe(401)
+ })
+})
+```
diff --git a/.agent/skills/tech-stack/elysiajs/references/validation.md b/.agent/skills/tech-stack/elysiajs/references/validation.md
new file mode 100644
index 0000000..ba723e0
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/validation.md
@@ -0,0 +1,491 @@
+# Validation Schema - SKILLS.md
+
+## What It Is
+Runtime validation + type inference + OpenAPI schema from single source. TypeBox-based with Standard Schema support.
+
+## Basic Usage
+```typescript
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .get('/id/:id', ({ params: { id } }) => id, {
+ params: t.Object({ id: t.Number({ minimum: 1 }) }),
+ response: {
+ 200: t.Number(),
+ 404: t.Literal('Not Found')
+ }
+ })
+```
+
+## Schema Types
+Third parameter of HTTP method:
+- **body** - HTTP message
+- **query** - URL query params
+- **params** - Path params
+- **headers** - Request headers
+- **cookie** - Request cookies
+- **response** - Response (per status)
+
+## Standard Schema Support
+Use Zod, Valibot, ArkType, Effect, Yup, Joi:
+```typescript
+import { z } from 'zod'
+import * as v from 'valibot'
+
+.get('/', ({ params, query }) => params.id, {
+ params: z.object({ id: z.coerce.number() }),
+ query: v.object({ name: v.literal('Lilith') })
+})
+```
+
+Mix validators in same handler.
+
+## Body
+```typescript
+body: t.Object({ name: t.String() })
+```
+
+GET/HEAD: body-parser disabled by default (RFC2616).
+
+### File Upload
+```typescript
+body: t.Object({
+ file: t.File({ format: 'image/*' }),
+ multipleFiles: t.Files()
+})
+// Auto-assumes multipart/form-data
+```
+
+### File (Standard Schema)
+```typescript
+import { fileType } from 'elysia'
+
+body: z.object({
+ file: z.file().refine((file) => fileType(file, 'image/jpeg'))
+})
+```
+
+Use `fileType` for security (validates magic number, not just MIME).
+
+## Query
+```typescript
+query: t.Object({ name: t.String() })
+// /?name=Elysia
+```
+
+Auto-coerces to specified type.
+
+### Arrays
+```typescript
+query: t.Object({ name: t.Array(t.String()) })
+```
+
+Formats supported:
+- **nuqs**: `?name=a,b,c` (comma delimiter)
+- **HTML form**: `?name=a&name=b&name=c` (multiple keys)
+
+## Params
+```typescript
+params: t.Object({ id: t.Number() })
+// /id/1
+```
+
+Auto-inferred as string if schema not provided.
+
+## Headers
+```typescript
+headers: t.Object({ authorization: t.String() })
+```
+
+`additionalProperties: true` by default. Always lowercase keys.
+
+## Cookie
+```typescript
+cookie: t.Cookie({
+ name: t.String()
+}, {
+ secure: true,
+ httpOnly: true
+})
+```
+
+Or use `t.Object`. `additionalProperties: true` by default.
+
+## Response
+```typescript
+response: t.Object({ name: t.String() })
+```
+
+### Per Status
+```typescript
+response: {
+ 200: t.Object({ name: t.String() }),
+ 400: t.Object({ error: t.String() })
+}
+```
+
+## Error Handling
+
+### Inline Error Property
+```typescript
+body: t.Object({
+ x: t.Number({ error: 'x must be number' })
+})
+```
+
+Or function:
+```typescript
+x: t.Number({
+ error({ errors, type, validation, value }) {
+ return 'Expected x to be number'
+ }
+})
+```
+
+### onError Hook
+```typescript
+.onError(({ code, error }) => {
+ if (code === 'VALIDATION')
+ return error.message // or error.all[0].message
+})
+```
+
+`error.all` - list all error causes. `error.all.find(x => x.path === '/name')` - find specific field.
+
+## Reference Models
+Name + reuse models:
+```typescript
+.model({
+ sign: t.Object({
+ username: t.String(),
+ password: t.String()
+ })
+})
+.post('/sign-in', ({ body }) => body, {
+ body: 'sign',
+ response: 'sign'
+})
+```
+
+Extract to plugin:
+```typescript
+// auth.model.ts
+export const authModel = new Elysia().model({ sign: t.Object({...}) })
+
+// main.ts
+new Elysia().use(authModel).post('/', ..., { body: 'sign' })
+```
+
+### Naming Convention
+Prevent duplicates with namespaces:
+```typescript
+.model({
+ 'auth.admin': t.Object({...}),
+ 'auth.user': t.Object({...})
+})
+```
+
+Or use `prefix` / `suffix` to rename models in current instance
+```typescript
+.model({ sign: t.Object({...}) })
+.prefix('model', 'auth')
+.post('/', () => '', {
+ body: 'auth.User'
+})
+```
+
+Models with `prefix` will be capitalized.
+
+## TypeScript Types
+```typescript
+const MyType = t.Object({ hello: t.Literal('Elysia') })
+type MyType = typeof MyType.static
+```
+
+Single schema → runtime validation + coercion + TypeScript type + OpenAPI.
+
+## Guard
+Apply schema to multiple handlers. Affects all handlers after definition.
+
+### Basic Usage
+```typescript
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .get('/none', ({ query }) => 'hi')
+ .guard({
+ query: t.Object({
+ name: t.String()
+ })
+ })
+ .get('/query', ({ query }) => query)
+ .listen(3000)
+```
+
+Ensures `query.name` string required for all handlers after guard.
+
+### Behavior
+| Path | Response |
+|---------------|----------|
+| /none | hi |
+| /none?name=a | hi |
+| /query | error |
+| /query?name=a | a |
+
+### Precedence
+- Multiple global schemas: latest wins
+- Global vs local: local wins
+
+### Schema Types
+
+1. override (default)
+Latest schema overrides collided schema.
+```typescript
+.guard({ query: t.Object({ name: t.String() }) })
+.guard({ query: t.Object({ id: t.Number() }) })
+// Only id required, name overridden
+```
+
+2. standalone
+Both schemas run independently. Both validated.
+```typescript
+.guard({ query: t.Object({ name: t.String() }) }, { type: 'standalone' })
+.guard({ query: t.Object({ id: t.Number() }) }, { type: 'standalone' })
+// Both name AND id required
+```
+
+# Typebox Validation (Elysia.t)
+
+Elysia.t = TypeBox with server-side pre-configuration + HTTP-specific types
+
+**TypeBox API mirrors TypeScript syntax** but provides runtime validation
+
+## Basic Types
+
+| TypeBox | TypeScript | Example Value |
+|---------|------------|---------------|
+| `t.String()` | `string` | `"hello"` |
+| `t.Number()` | `number` | `42` |
+| `t.Boolean()` | `boolean` | `true` |
+| `t.Array(t.Number())` | `number[]` | `[1, 2, 3]` |
+| `t.Object({ x: t.Number() })` | `{ x: number }` | `{ x: 10 }` |
+| `t.Null()` | `null` | `null` |
+| `t.Literal(42)` | `42` | `42` |
+
+## Attributes (JSON Schema 7)
+
+```ts
+// Email format
+t.String({ format: 'email' })
+
+// Number constraints
+t.Number({ minimum: 10, maximum: 100 })
+
+// Array constraints
+t.Array(t.Number(), {
+ minItems: 1, // min items
+ maxItems: 5 // max items
+})
+
+// Object - allow extra properties
+t.Object(
+ { x: t.Number() },
+ { additionalProperties: true } // default: false
+)
+```
+
+## Common Patterns
+
+### Union (Multiple Types)
+```ts
+t.Union([t.String(), t.Number()])
+// type: string | number
+// values: "Hello" or 123
+```
+
+### Optional (Field Optional)
+```ts
+t.Object({
+ x: t.Number(),
+ y: t.Optional(t.Number()) // can be undefined
+})
+// type: { x: number, y?: number }
+// value: { x: 123 } or { x: 123, y: 456 }
+```
+
+### Partial (All Fields Optional)
+```ts
+t.Partial(t.Object({
+ x: t.Number(),
+ y: t.Number()
+}))
+// type: { x?: number, y?: number }
+// value: {} or { y: 123 } or { x: 1, y: 2 }
+```
+
+## Elysia-Specific Types
+
+### UnionEnum (One of Values)
+```ts
+t.UnionEnum(['rapi', 'anis', 1, true, false])
+```
+
+### File (Single File Upload)
+```ts
+t.File({
+ type: 'image', // or ['image', 'video']
+ minSize: '1k', // 1024 bytes
+ maxSize: '5m' // 5242880 bytes
+})
+```
+
+**File unit suffixes**:
+- `m` = MegaByte (1048576 bytes)
+- `k` = KiloByte (1024 bytes)
+
+### Files (Multiple Files)
+```ts
+t.Files() // extends File + array
+```
+
+### Cookie (Cookie Jar)
+```ts
+t.Cookie({
+ name: t.String()
+}, {
+ secrets: 'secret-key' // or ['key1', 'key2'] for rotation
+})
+```
+
+### Nullable (Allow null)
+```ts
+t.Nullable(t.String())
+// type: string | null
+```
+
+### MaybeEmpty (Allow null + undefined)
+```ts
+t.MaybeEmpty(t.String())
+// type: string | null | undefined
+```
+
+### Form (FormData Validation)
+```ts
+t.Form({
+ someValue: t.File()
+})
+// Syntax sugar for t.Object with FormData support
+```
+
+### UInt8Array (Buffer → Uint8Array)
+```ts
+t.UInt8Array()
+// For binary file uploads with arrayBuffer parser
+```
+
+### ArrayBuffer (Buffer → ArrayBuffer)
+```ts
+t.ArrayBuffer()
+// For binary file uploads with arrayBuffer parser
+```
+
+### ObjectString (String → Object)
+```ts
+t.ObjectString()
+// Accepts: '{"x":1}' → parses to { x: 1 }
+// Use in: query string, headers, FormData
+```
+
+### BooleanString (String → Boolean)
+```ts
+t.BooleanString()
+// Accepts: 'true'/'false' → parses to boolean
+// Use in: query string, headers, FormData
+```
+
+### Numeric (String/Number → Number)
+```ts
+t.Numeric()
+// Accepts: '123' or 123 → transforms to 123
+// Use in: path params, query string
+```
+
+## Elysia Behavior Differences from TypeBox
+
+### 1. Optional Behavior
+
+In Elysia, `t.Optional` makes **entire route parameter** optional (not object field):
+
+```ts
+.get('/optional', ({ query }) => query, {
+ query: t.Optional( // makes query itself optional
+ t.Object({ name: t.String() })
+ )
+})
+```
+
+**Different from TypeBox**: TypeBox uses Optional for object fields only
+
+### 2. Number → Numeric Auto-Conversion
+
+**Route schema only** (not nested objects):
+
+```ts
+.get('/:id', ({ id }) => id, {
+ params: t.Object({
+ id: t.Number() // ✅ Auto-converts to t.Numeric()
+ }),
+ body: t.Object({
+ id: t.Number() // ❌ NOT converted (stays t.Number())
+ })
+})
+
+// Outside route schema
+t.Number() // ❌ NOT converted
+```
+
+**Why**: HTTP headers/query/params always strings. Auto-conversion parses numeric strings.
+
+### 3. Boolean → BooleanString Auto-Conversion
+
+Same as Number → Numeric:
+
+```ts
+.get('/:active', ({ active }) => active, {
+ params: t.Object({
+ active: t.Boolean() // ✅ Auto-converts to t.BooleanString()
+ }),
+ body: t.Object({
+ active: t.Boolean() // ❌ NOT converted
+ })
+})
+```
+
+## Usage Pattern
+
+```ts
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .post('/', ({ body }) => `Hello ${body}`, {
+ body: t.String() // validates body is string
+ })
+ .listen(3000)
+```
+
+**Validation flow**:
+1. Request arrives
+2. Schema validates against HTTP body/params/query/headers
+3. If valid → handler executes
+4. If invalid → Error Life Cycle
+
+## Notes
+
+[Inference] Based on docs:
+- TypeBox mirrors TypeScript but adds runtime validation
+- Elysia.t extends TypeBox with HTTP-specific types
+- Auto-conversion (Number→Numeric, Boolean→BooleanString) only for route schemas
+- Use `t.Optional` for optional route params (different from TypeBox behavior)
+- File validation supports unit suffixes ('1k', '5m')
+- ObjectString/BooleanString for parsing strings in query/headers
+- Cookie supports key rotation with array of secrets
diff --git a/.agent/skills/tech-stack/elysiajs/references/websocket.md b/.agent/skills/tech-stack/elysiajs/references/websocket.md
new file mode 100644
index 0000000..b2c86a8
--- /dev/null
+++ b/.agent/skills/tech-stack/elysiajs/references/websocket.md
@@ -0,0 +1,250 @@
+# WebSocket
+
+## Basic WebSocket
+
+```typescript
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send(message) // Echo back
+ }
+ })
+ .listen(3000)
+```
+
+## With Validation
+
+```typescript
+import { Elysia, t } from 'elysia'
+
+.ws('/chat', {
+ body: t.Object({
+ message: t.String(),
+ username: t.String()
+ }),
+ response: t.Object({
+ message: t.String(),
+ timestamp: t.Number()
+ }),
+ message(ws, body) {
+ ws.send({
+ message: body.message,
+ timestamp: Date.now()
+ })
+ }
+})
+```
+
+## Lifecycle Events
+
+```typescript
+.ws('/chat', {
+ open(ws) {
+ console.log('Client connected')
+ },
+ message(ws, message) {
+ console.log('Received:', message)
+ ws.send('Echo: ' + message)
+ },
+ close(ws) {
+ console.log('Client disconnected')
+ },
+ error(ws, error) {
+ console.error('Error:', error)
+ }
+})
+```
+
+## Broadcasting
+
+```typescript
+const connections = new Set()
+
+.ws('/chat', {
+ open(ws) {
+ connections.add(ws)
+ },
+ message(ws, message) {
+ // Broadcast to all connected clients
+ for (const client of connections) {
+ client.send(message)
+ }
+ },
+ close(ws) {
+ connections.delete(ws)
+ }
+})
+```
+
+## With Authentication
+
+```typescript
+.ws('/chat', {
+ beforeHandle({ headers, status }) {
+ const token = headers.authorization?.replace('Bearer ', '')
+ if (!verifyToken(token)) {
+ return status(401)
+ }
+ },
+ message(ws, message) {
+ ws.send(message)
+ }
+})
+```
+
+## Room-Based Chat
+
+```typescript
+const rooms = new Map>()
+
+.ws('/chat/:room', {
+ open(ws) {
+ const room = ws.data.params.room
+ if (!rooms.has(room)) {
+ rooms.set(room, new Set())
+ }
+ rooms.get(room)!.add(ws)
+ },
+ message(ws, message) {
+ const room = ws.data.params.room
+ const clients = rooms.get(room)
+
+ if (clients) {
+ for (const client of clients) {
+ client.send(message)
+ }
+ }
+ },
+ close(ws) {
+ const room = ws.data.params.room
+ const clients = rooms.get(room)
+
+ if (clients) {
+ clients.delete(ws)
+ if (clients.size === 0) {
+ rooms.delete(room)
+ }
+ }
+ }
+})
+```
+
+## With State/Context
+
+```typescript
+.ws('/chat', {
+ open(ws) {
+ ws.data.userId = generateUserId()
+ ws.data.joinedAt = Date.now()
+ },
+ message(ws, message) {
+ const response = {
+ userId: ws.data.userId,
+ message,
+ timestamp: Date.now()
+ }
+ ws.send(response)
+ }
+})
+```
+
+## Client Usage (Browser)
+
+```typescript
+const ws = new WebSocket('ws://localhost:3000/chat')
+
+ws.onopen = () => {
+ console.log('Connected')
+ ws.send('Hello Server!')
+}
+
+ws.onmessage = (event) => {
+ console.log('Received:', event.data)
+}
+
+ws.onerror = (error) => {
+ console.error('Error:', error)
+}
+
+ws.onclose = () => {
+ console.log('Disconnected')
+}
+```
+
+## Eden Treaty WebSocket
+
+```typescript
+// Server
+export const app = new Elysia()
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send(message)
+ }
+ })
+
+export type App = typeof app
+
+// Client
+import { treaty } from '@elysiajs/eden'
+import type { App } from './server'
+
+const api = treaty('localhost:3000')
+const chat = api.chat.subscribe()
+
+chat.subscribe((message) => {
+ console.log('Received:', message)
+})
+
+chat.send('Hello!')
+```
+
+## Headers in WebSocket
+
+```typescript
+.ws('/chat', {
+ header: t.Object({
+ authorization: t.String()
+ }),
+ beforeHandle({ headers, status }) {
+ const token = headers.authorization?.replace('Bearer ', '')
+ if (!token) return status(401)
+ },
+ message(ws, message) {
+ ws.send(message)
+ }
+})
+```
+
+## Query Parameters
+
+```typescript
+.ws('/chat', {
+ query: t.Object({
+ username: t.String()
+ }),
+ message(ws, message) {
+ const username = ws.data.query.username
+ ws.send(`${username}: ${message}`)
+ }
+})
+
+// Client
+const ws = new WebSocket('ws://localhost:3000/chat?username=john')
+```
+
+## Compression
+
+```typescript
+new Elysia({
+ websocket: {
+ perMessageDeflate: true
+ }
+})
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send(message)
+ }
+ })
+```
diff --git a/.agent/skills/tech-stack/shadcn-ui-designer/SKILL.md b/.agent/skills/tech-stack/shadcn-ui-designer/SKILL.md
new file mode 100644
index 0000000..eeacccf
--- /dev/null
+++ b/.agent/skills/tech-stack/shadcn-ui-designer/SKILL.md
@@ -0,0 +1,477 @@
+---
+name: shadcn-ui-designer
+description: Design and build modern UI components and pages using shadcn/ui. Creates clean, accessible interfaces with Tailwind CSS following shadcn principles. Use when building UI components, pages, forms, dashboards, or any interface work.
+---
+
+# Shadcn UI Designer
+
+Build production-ready UI components using shadcn/ui principles: minimal, accessible, composable, and beautiful by default.
+
+## Core Philosophy
+
+**Design modern, minimal interfaces** with:
+- Clean typography (Inter/system fonts, 2-3 weights max)
+- Ample whitespace (4px-based spacing: p-1 through p-8)
+- Subtle shadows (shadow-sm/md/lg only)
+- Accessible contrast (WCAG AA minimum)
+- Smooth micro-interactions (200-300ms transitions)
+- Professional neutrals (slate/zinc scale) with subtle accents
+
+**Build composable components** that work together seamlessly.
+
+## Quick Start Pattern
+
+```tsx
+import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+
+export function MyComponent() {
+ return (
+
+
+
Title
+
Description
+
+
+
+
+ Section
+
+
+ {/* Content here */}
+
+
+
+ )
+}
+```
+
+## Design System Rules
+
+### Typography
+- **Hierarchy**: `text-2xl` (headings) → `text-base` (body) → `text-sm` (secondary)
+- **Weights**: `font-semibold` (600) for emphasis, `font-medium` (500) for labels, `font-normal` (400) for body
+- **Colors**: `text-foreground` (primary), `text-muted-foreground` (secondary)
+
+```tsx
+
Page Title
+
Supporting text
+```
+
+### Spacing
+Use consistent spacing scale:
+- **Micro**: `space-y-2` (8px) - within sections
+- **Small**: `space-y-4` (16px) - between elements
+- **Medium**: `space-y-6` (24px) - between sections
+- **Large**: `space-y-8` (32px) - major divisions
+
+```tsx
+