---
name: lunora
description: Routes general Lunora requests to the right project skill. Use when the user
    asks which Lunora skill to use, or gives an underspecified task for a Lunora
    app (a type-safe, real-time backend on Cloudflare Workers + Durable Objects
    with a Vite-first DX).
---

# Lunora

Use this as the routing skill for Lunora work in this repo.

Lunora exposes a Convex-style functional API (`defineSchema`, `query`,
`mutation`, `action`) on top of Cloudflare Workers and Durable Objects. State
lives in a per-app `ShardDO` (SQLite, OCC, hibernated WebSocket subscriptions)
by default; `.shardBy(key)` partitions it across many DOs and `.global()`
replicates a table to D1 — or to Postgres/MySQL over Hyperdrive — for
low-latency cross-region reads. A Vite plugin drives codegen and end-to-end
type sync.

If a more specific Lunora skill clearly matches the request, use that instead.

## Start Here

Before writing or changing any `lunora/` code, make sure the generated types are
current — they are the contract the client and server share.

```bash
lunora codegen
```

This regenerates `lunora/_generated/` (`api.ts`, `server.ts`, `dataModel.ts`,
`shard.ts`, `openapi.ts`, …) from `lunora/schema.ts` and your function files. The
output typechecks your schema and functions, so it doubles as the agent's main
feedback loop after each edit. Commit `lunora/_generated/` — it is part of the
source tree, not a build artifact to gitignore.

If a project-level `AGENTS.md` / `CLAUDE.md` exists, read it first — it overrides
these defaults.

## Route to the Right Skill

After codegen is green, use the most specific Lunora skill for the task:

- New project, or adding Lunora to an existing app: `lunora-quickstart`
- Writing or reviewing schema + functions (the core authoring rules):
  `lunora-functions`
- Wiring live data into a client (hooks, optimistic updates): `lunora-realtime`
- Authentication setup (email/password, OAuth, magic link, OTP):
  `lunora-setup-auth`
- Transactional email: `lunora-setup-mail`
- R2 file storage (signed upload/download): `lunora-setup-storage`
- Deferred work (`ctx.scheduler`) and cron jobs: `lunora-setup-scheduler`
- Querying an **existing** Postgres/MySQL database from an action (`ctx.sql`,
  non-reactive): `lunora-setup-hyperdrive`
- Using Postgres/MySQL as a **reactive `.global()` backend**, or migrating a D1
  `.global()` dataset onto it: `lunora-setup-hyperdrive-global`
- Building a reusable capability — a registry item or an `@lunora/*` package:
  `lunora-create-package`
- Planning or running a schema/data migration: `lunora-migration-helper`
- Deploying to Cloudflare (wrangler, bindings, secrets, the drift gate):
  `lunora-deploy`
- Investigating performance, scan, or write-conflict issues:
  `lunora-performance-audit`

**Reviewing or auditing existing `lunora/` code** has no skill of its own,
because two commands do the work mechanically — run them before reading
anything:

```bash
lunora verify    # wrangler config + codegen dry-run + tsc --noEmit
lunora advisor   # the static + runtime lint set, scored per procedure
```

`lunora advisor` is the security/quality review: RLS coverage, ownership and
identity checks, fail-open guards, input validators, unindexed reads,
non-deterministic calls in queries, SQL interpolation, and leaked secrets. It
prints each finding's own lint id and category, so read its output rather than
grepping for rule names. Use `--entry <file>#<export>` to inspect one procedure
and `--min-score` / `--baseline` to gate CI. Only after it is clean is a by-hand
pass over `lunora-functions` worth the tokens.

If one of those clearly matches the user's goal, switch to it instead of staying
in this skill.

## Capabilities Without a Dedicated Skill

Most other capabilities install as a **registry item** — `lunora registry add
<item>` scaffolds the `lunora/` glue, wrangler bindings, and env vars, then
prints post-install steps. Browse with `lunora registry list`; preview with
`lunora registry view <item>`. Read the installed item's README, and the
package's `docs/` for the API.

| Goal                                          | Install / package                                            |
| --------------------------------------------- | ------------------------------------------------------------ |
| Background jobs on Cloudflare Queues          | `registry add queue` → `@lunora/queue` (`ctx.queues`)        |
| Durable multi-step workflows                  | `registry add workflow` → `@lunora/workflow` (`ctx.runStep`) |
| Durable AI agents (tool loops, HITL, memory)  | `@lunora/agent` (`defineAgent`)                              |
| Workers AI / RAG                              | `registry add ai` → `@lunora/ai` (`ctx.ai`, `defineRag`)     |
| Feature flags (OpenFeature)                   | `registry add flags` → `@lunora/flags` (`ctx.flags`)         |
| Payments (Stripe / Polar)                     | `registry add payment` → `@lunora/payment`                   |
| Rate limiting                                 | `registry add ratelimit` → `@lunora/ratelimit`               |
| Headless browser (action-only)                | `registry add browser` → `@lunora/browser` (`ctx.browser`)   |
| Cloudflare Containers                         | `@lunora/container` (`defineContainer`, `ctx.containers`)    |
| Presence / who's-here                         | `registry add presence`                                      |
| Cloudflare Access (Zero Trust) identity       | `registry add cloudflare-access`                             |
| Backup / restore                              | `registry add backup`                                        |
| Testing (in-memory harness, agent doubles)    | `@lunora/testing` (`lunoraTest`)                             |
| Deterministic seed data                       | `@lunora/seed` + `lunora seed`                               |
| Local-first replica / offline mirror          | `@lunora/replica`                                            |
| Exposing the deployment to AI agents over MCP | `@lunora/mcp`                                                |

Scaffolding inside this repo uses `vis generate lunora-<kind>` — `query`,
`mutation`, `action`, `http-route`, `table`, `cron`, `container`, `workflow`,
`queue`, `step`, `agent`, `flags`, `collections`, `package` (`vis generate
--list` for the full set). Most take a name: **always** pass it as
`--name=value`, since vis parses a space-separated `--name foo` as `--name=true`
plus a stray positional. `lunora-flags` and `lunora-collections` are singletons
and take no name.

## Core Mental Model

- **Functions** live in `lunora/*.ts` and are one of `query` (reactive read),
  `mutation` (transactional write), or `action` (side effects / `fetch` / no
  direct db). `internalQuery` / `internalMutation` / `internalAction` are the
  non-public variants.
- **Schema** lives in `lunora/schema.ts` via `defineSchema` + `defineTable`,
  with validators from `v.*` (re-exported by `@lunora/server`).
- **Reads go through indexes.** Prefer `ctx.db.query("t").withIndex(...)` over
  `.filter(...)`; declare the index with `.index("by_x", ["x"])`.
- **Clients** subscribe over WebSocket. `useQuery`/`useMutation` (React, Vue,
  Solid, Svelte, React Native; signals in Angular) re-render the moment a
  mutation changes the queried rows. Subscriptions run under the socket's
  verified identity, so `rls()` / `ctx.auth` apply to live updates too.

## When Not to Use

- The user has already named a more specific Lunora workflow.
- Another Lunora skill obviously fits the request better.
