08. reeve
The CLI. Built with Ink, running on Bun, compiled to a single binary.
Bare ./reeve opens a TUI. Any subcommand with arguments skips it entirely.
The TUI
┌─ avelon ────────────────────────────── my-app · supabase · next · local ─┐
│ │
│ Make model, controller, request, policy, ward, event, listener, │
│ errand, action, command, middleware, view, driver, package │
│ Database migrate, rollback, fresh, seed, schema:pull │
│ Wards list, sync, check, explain │
│ Routes list, sync │
│ Errands work, failed, retry, flush │
│ Inspect event:list, config:show, capabilities, about │
│ Doctor bailiff, docs:check, ward:check, types:check, env:check │
│ │
│ ↑↓ navigate · ⏎ select · / search · q quit │
└───────────────────────────────────────────────────────────────────────────┘
Rules:
- Every TUI path prints the equivalent non-interactive command before running it. People learn
the scriptable form by using the friendly one.
- Any subcommand invoked with arguments skips the TUI. CI never sees an interactive prompt.
--no-tuiand a non-TTY stdout both force plain mode.- The header always shows app name, database driver, adapter, and environment. "Which database am I
pointed at" is the question that precedes most disasters.
- Destructive commands name what they will destroy and require typing the environment name when it
is not local.
Generators
reeve make:model Post -mfcpw # model + migration + factory + controller + policy + ward
reeve make:controller PostController --resource
reeve make:request StorePostRequest
reeve make:policy PostPolicy --model=Post
reeve make:ward PostWard --model=Post
reeve make:event PostPublished
reeve make:listener NotifySubscribers --event=PostPublished --queued
reeve make:errand GenerateThumbnail
reeve make:action PublishPost
reeve make:middleware EnsureSubscribed
reeve make:command PruneDrafts
reeve make:view posts/Index
reeve make:auth # routes, controllers, requests, views for sign in and up
reeve make:driver storage r2 # driver package with conformance suite wired
reeve make:package billing # package skeleton with README in house style
reeve stub:publish ejects the generator templates into stubs/ so a team can change the shape of generated code without forking the framework.
Database and wards
reeve migrate
reeve migrate:rollback
reeve migrate:fresh --seed
reeve migrate:status
reeve make:migration create_posts_table
reeve schema:pull # regenerate database/types.ts from the live schema
reeve db:seed
reeve ward:list # every model, its ward, and enforcement paths
reeve ward:sync # compile wards into a migration
reeve ward:check # fail on missing wards or drift
reeve ward:explain Post read # print the predicate and the SQL it compiles to
ward:explain exists because a compiled policy you cannot read is a policy you cannot trust.
Routes and events
reeve route:list # method, URI, name, action, middleware
reeve route:sync # regenerate the adapter's router
reeve event:list # events, listeners, delivery modes, priorities
Errands
reeve errand:work # drain loop, local
reeve errand:failed
reeve errand:retry <id|all>
reeve errand:flush
Serverless functions cannot hold a daemon, so the deployed worker is a drain-and-exit route handler invoked on a schedule. errand:work runs the same drain logic in a real loop locally, so the code path is identical in both places.
Doctor
reeve doctor
Aggregates everything that should be true before a push:
- Bailiff clean
- Ward drift
- Type drift between models and the live schema
- Missing environment variables
- Migration status
- Capability matrix for the resolved config, diffed against the previous environment
One command, one exit code.
reeve capabilities
Prints what the configured drivers can and cannot do, which is the fastest answer to "why does DB.transaction not exist."
Inspect
reeve tinker # REPL with models, DB, and helpers preloaded
reeve config:show # resolved config with secrets redacted
reeve about # versions, adapter, drivers, runtime
Documentation
reeve docs:check
reeve docs:check packages/orm
reeve docs:check --workspace
Lints package READMEs against the house style in 10-conventions.md. A package fails when a required section is missing, a TypeScript or JSON code block does not parse, or a public export is absent from the Method Reference table. Waive a section or export with <!-- docs:check-waiver: section Testing --> or <!-- docs:check-waiver: export name -->. make:package scaffolds a README that already passes.