CLI
@beignet/cli creates Beignet apps, generates feature slices and workflow
artifacts, runs database and operational entrypoints, inspects route wiring,
and catches drift after manual edits. Use Quickstart to
create and run your first app; use this page as the command reference.
Beignet requires Node.js 22.12 or newer. Bun is optional; npm, pnpm, Yarn, and Bun can run CLI commands.
Scaffold a new app through your package manager's create command. Inside a
generated app, @beignet/cli is already a dev dependency with a beignet
package script. Or run the scoped package without installing:
bun create beignet my-app
bun beignet lint
npm run beignet -- lint
bunx @beignet/cli doctor --strict
npx @beignet/cli doctor --strictAlways pass the scoped name (@beignet/cli) to bunx, npx, pnpm dlx, or
yarn dlx — the unscoped npm name beignet belongs to an unrelated package.
Reference blocks below show the bare beignet <command> syntax; prefix it
with your package manager. beignet --version prints the installed CLI
version.
Commands
| Command | What it does |
|---|---|
beignet create [directory] | Scaffold a new Beignet app. |
beignet make <generator> <name> | Generate feature slices and workflow artifacts. |
beignet db <subcommand> | Run the app's database lifecycle scripts. |
beignet routes | Inspect contract-to-route wiring. |
beignet map | Map app architecture and workflow relationships. |
beignet explain <kind> <target> | Explain one mapped concept with source evidence. |
beignet check | Run the full validation loop as one command. |
beignet lint | Enforce architecture dependency direction. |
beignet doctor | Report framework drift, optionally fixing it. |
beignet provider add <preset> | Add provider dependencies, wiring, env examples, and setup notes. |
beignet provider audit | Inventory installed provider setup without failing CI. |
beignet task run <name> | Run an app-owned operational task. |
beignet schedule run <name> | Run an app-owned schedule once. |
beignet outbox drain | Run one bounded outbox drain pass. |
beignet mcp | Run an MCP server exposing CLI tools to coding agents. |
beignet completion <install|uninstall> | Manage bash or zsh completions. |
create
bun create beignet my-appIn an interactive terminal without selection flags, create prompts for the
project directory, whether the app is API-only, which database to use, and
which providers to add. A selection flag (--api, --db, or
--providers) skips the prompts, --yes forces the defaults, and non-TTY
environments never see prompts. There is one full-stack starter; --api
drops the UI shell and app pages while keeping the same architecture, and
--db picks the database backend (sqlite by default). The CLI writes files
only — see Quickstart for what the starter contains and
the install, environment, migrate, and first-run steps, and
Database and transactions for what each --db backend
scaffolds.
Better Auth, Drizzle persistence, Pino, and no-op error reporting are part of
every starter, so passing them to --providers is an error. Providers
add external service providers on top: the provider package, peer dependencies,
wiring in server/providers.ts, .env.example entries, and setup notes in
docs/providers.md.
| Provider | Adds |
|---|---|
jobs-inngest | @beignet/core/jobs, @beignet/provider-jobs-inngest, and inngest |
mail-resend | @beignet/provider-mail-resend and resend |
rate-limit-upstash | @beignet/provider-rate-limit-upstash, @upstash/ratelimit, and @upstash/redis |
--providers accepts the full provider add
preset catalog. Presets are applied to the fresh scaffold with the same
machinery as beignet provider add, so create-time and post-create setup
stay identical; examples include event-bus-redis, cache-redis,
jobs-bullmq, storage-s3, and search-meilisearch. Starter-native presets
(jobs-inngest, mail-resend, and rate-limit-upstash) are rendered directly,
and selections that fill the same app port (for example mail-resend plus
mail-smtp) fail before any files are written. The interactive prompt keeps
the short template list and points at the wider catalog.
create --dry-run applies those same preset transformations in memory, so
its human and JSON file lists match a real create without writing the target
directory.
| Option | Description |
|---|---|
--template <name> | App template. next is the only template today. |
--api | Scaffold an API-only app without the UI shell. |
--db <database> | Database backend: sqlite (default), postgres, or mysql. |
--package-manager <pm> | bun, npm, pnpm, or yarn, used in printed next steps. |
--providers <names> | One value or a comma-separated list of provider presets. |
--yes | Skip interactive prompts and use the defaults. |
--force | Write into a non-empty directory. |
--dry-run | Preview planned writes without creating files. |
--json | Print the plan or result as JSON. |
make
Generators run inside an app and target the canonical structure described in
App architecture. All of them — along with routes,
map, lint, and doctor — resolve beignet.config.* (.ts, .json, .mjs, or
.js) first. Set framework: "next" (the default) for @beignet/next apps or
framework: "web" for servers built on the standard Fetch adapter from
@beignet/web. In the web profile, routes and doctor derive the exposed
HTTP surface from canonical feature route groups in the central
defineRoutes(...) registry passed to createFetchServer(...); they do not
require app/api/ or Next.js route handlers. Vite and Bun do not need separate
values because they are client tooling and a Fetch host rather than Beignet
server adapters.
Omitted paths fall back to the generated defaults. The same config can declare
app-owned operational table names, such
as database.tables.audit: "audit_events", so doctor checks Drizzle-backed
audit, idempotency, and outbox wiring against the names your app uses. Add
database.schemaSources when those table definitions live in a shared package
or non-standard app path, for example ["@acme/db/schema"]. Apps that satisfy
a provider requirement through an injected platform client can list that
specific static-audit exception under
providerAudit.ignoreRequiredEnv; runtime env validation is unchanged.
Generators are idempotent: repeated runs skip identical files and avoid
duplicate wiring, and a generated file that diverged stops the command unless
you pass --force.
Framework-neutral generators such as make feature, make contract, and
make schedule work in both profiles. Commands that currently generate
Next.js handlers — make payments, make upload, make outbox, and
make schedule --route — reject framework: "web" before writing files.
create also remains a Next.js scaffold today.
Every make command accepts:
| Option | Description |
|---|---|
--dry-run | Preview generated changes without writing files. |
--json | Print the planned or written changes as JSON. |
--force | Overwrite generated files that diverged. |
--cwd <dir> | Generate against an app root in another directory. |
make feature
beignet make feature projectsGenerates the contract-first vertical slice for a product capability:
contracts.ts, schemas.ts, use-cases/, ports.ts, routes.ts, a test
file, and a Drizzle repository adapter. It registers the route group in
server/routes.ts, the port in ports/index.ts, and the repository in
infra/db/repositories.ts; OpenAPI routes that use route registration stay in
sync automatically. The generated name field is a placeholder — reshape the
slice around the real workflow.
| Option | Description |
|---|---|
--with <addons> | Adds feature-owned artifacts: policy, factory, seed, task, event, listener, job, notification, schedule, ui, or upload. |
--recipe full-slice | Adds the canonical full-slice recipe: policy, factory, seed, task, event, listener, job, notification, schedule, UI client helpers, component, upload, and outbox wiring. |
Each addon writes the same output as the matching standalone generator; ui
writes feature-colocated React Query helpers and a contract-backed React Hook
Form component. The generated form uses the contract body schema for field
validation, maps mutation failures to the root form error, resets after
success, and invalidates the generated list query. It creates the canonical
client/forms.ts adapter and adds the required form dependencies when they are
missing. When ui and upload are both selected, the upload addon also emits
a typed React upload client, uploader component, and component test.
Use --recipe full-slice when you want a richer reference slice:
beignet make feature projects --recipe full-sliceThe recipe keeps the base contracts.ts, schemas.ts, use-cases/,
ports.ts, routes.ts, and tests, then adds feature-owned workflow artifacts
around them. The generated create use case publishes the generated
ProjectCreated event, and the event/job generators add server/outbox.ts,
infra/db/schema/outbox-messages.ts, the outbox drain route, and the required
port wiring. The listener generator registers the feature listener registry in
server/listeners.ts and wires the central listener provider in
server/providers.ts. Replace the starter name field, logger messages,
listener body, job body, notification payload, and task body with the real
workflow. When the workflow needs durable audit records, follow
Audit and activity logging and record the business action inside the
same Unit of Work transaction as the write.
make resource
beignet make resource projects
beignet make resource projects --authorization --tenant-scoped --events --soft-deleteGenerates a CRUD-shaped slice when the concept is an entity with
repository-backed persistence: list, create, get, update, and delete
contracts, use cases, route handlers, repository methods, a policy starter,
tests, feature-specific not-found and conflict catalog errors, a Drizzle
schema file, and repository registration. Generated list endpoints use cursor
pagination with an explicit query transport shared by the client, server, and
OpenAPI document. Updates use optimistic concurrency version checks that turn
stale writes into the generated conflict error. See
Build your first feature for the guided flow.
| Option | Description |
|---|---|
--authorization | Authorization metadata, policy wiring, ctx.gate.authorize(...) checks, and a policy matrix test. |
--tenant-scoped | Tenant-scoped schemas, TenantScope repository boundaries, and use-case checks. |
--events | Created, updated, and deleted domain events published through ctx.ports.eventBus. |
--soft-delete | Archive rows with deletedAt instead of hard-deleting. |
--events also wires the event bus when the app has none, exactly like
make event (see workflow generators below).
make contract
beignet make contract projectsWrites features/projects/contracts.ts with a starter contract group, schema,
and standard error response. It does not wire routes, use cases, or ports —
use make feature for the full slice. See Contracts.
make use-case and make test
beignet make use-case projects.archive-project
beignet make test projects.archive-projectmake use-case writes features/projects/use-cases/archive-project.ts and
updates the use-case index; actions starting with get, list, find,
search, or count generate .query(...), others .command(...). make test writes features/projects/tests/archive-project.test.ts using the test
context helpers. See Application and Testing.
make port, make adapter, and make policy
beignet make port email
beignet make adapter email
beignet make policy postsmake port writes ports/email.ts, adds the port to AppPorts, creates a
test fake, and wires a throwing infra stub so the app still typechecks. make adapter writes infra/email/email-adapter.ts and replaces the stub; it stops
instead of guessing when the infra wiring was customized. make policy writes
features/posts/policy.ts with a definePolicy(...) starter. See
Ports and Authorization.
Workflow generators
beignet make event posts.published
beignet make listener posts.enqueue-published-email --event posts.published
beignet make job posts.send-published-email
beignet make notification posts.published
beignet make outbox
beignet make schedule posts.daily-summary --cron "0 9 * * *" --timezone America/Chicago --route
beignet make task posts.backfill-search
beignet make upload posts.attachment
beignet make upload posts.attachment --uiNames use feature.name format. Each generator writes the colocated feature
file (for example features/posts/jobs/send-published-email.ts), creates or
updates the folder's registry index.ts (postEvents, postJobs, and so
on), and creates the matching app-bound lib/ builder such as lib/jobs.ts
on first use. New apps do not scaffold workflow folders; generators create
them on demand. They also keep central registries and ports wired:
make scheduleandmake taskcreate or update theserver/schedules.tsandserver/tasks.tsregistries used by the runners below.make listenercreates or updates theserver/listeners.tsregistry and wires that central registry withregisterListeners(...). Its generated provider registers instart(), awaits the registry's initial readiness with the default 10-second deadline, and awaits cleanup instop().make outboxcreatesserver/outbox.tsand a boundedapp/api/cron/outbox/drainroute. On Next.js 15.1 or newer it also adds a server-local provider that wraps the database Unit of Work with a push-assistedafter()drain; earlier versions keep cron-only wiring. Themake eventandmake jobcommands create that outbox path on first use and append the feature's registries todefineOutboxRegistry({...}). See Outbox.make jobensuresjobs: JobDispatcherPortis declared and bound. It installs an app-owned inline provider only when no direct or deferred job dispatcher exists, preserving providers such as Inngest. Adding an Inngest or BullMQ preset later replaces only the marked generated fallback and rejects unmarked custom inline wiring as a conflict.make eventandmake resource --eventswireeventBus: EventBusPortintoAppPorts, registercreateMemoryEventBusProvider()inserver/providers.ts, and add the@beignet/provider-event-bus-memorydependency when the ports file lacks the key.make notificationwiresmailer: MailerPortwithcreateMemoryMailerProvider()andnotifications: NotificationPortwithcreateInlineNotificationsProvider(), each independently and only when missing, so providers such as Resend and app-owned adapters are left untouched. Swap the dev-default providers when you outgrow them.make listenerrequires--eventand expects the event file to exist; runmake eventfirst.make schedule --routewritesapp/api/cron/<feature>/<name>/route.ts, which requiresCRON_SECRET, and adds a generatedCRON_SECRETtolib/env.tsand.env.examplewhen the app does not define one.make upload --uiadds a shared client/server constraint manifest, typed upload client, React uploader, and component test. It is available in full-stack apps; API-only apps keep the backend upload workflow.make uploadvalidates central registration before writing. If a customized upload route no longer containsuploadRegistry = defineUploads({ ... }), it fails with the registry entry to add manually instead of leaving a generated upload unregistered.
| Command | Options |
|---|---|
make listener | --event <feature.event> (required). |
make schedule | --cron <expression> (defaults to 0 9 * * *), --timezone <zone>, --route. |
make upload | --ui to add the connected React upload workflow. |
Concept pages: Events, Jobs, Schedules, Notifications, and Uploads. Operational tasks are app-owned entrypoints for backfills, maintenance, and one-off repair work; they should call use cases or ports rather than copying business rules into scripts.
make factory and make seed
beignet make factory posts.post
beignet make seed posts.demo-postsmake factory writes features/posts/tests/factories/post.ts plus a factory
registry; the starter persists through repository ports. make seed writes
features/posts/seeds/demo-posts.ts plus a seed registry, creates the
app-owned server/seed.ts entrypoint when it is missing, and adds the
db:seed script. The entrypoint lives in server/ because it boots the app
through getServer(), which beignet lint forbids from infra/. See
Database.
make tenancy
beignet make tenancy
beignet db generate
beignet db migrate
beignet db statusGenerates a workspace tenancy slice for Drizzle-backed apps: a
features/workspaces feature with contracts, use cases, a membership-aware
policy, repositories for workspaces, members, and invites, three Drizzle
tables, invite email notifications, tests, and a demo-workspace seed with a
sign-in-able demo admin. It replaces the starter lib/tenant.ts and
server/context.ts so every request resolves the active workspace from the
user's memberships and the beignet-workspace cookie, and adds
membership to the app context. On apps with the frontend shell it also
emits workspace and member settings pages, an invite accept screen, a
workspace switcher, and settings navigation entries.
make tenancy only replaces lib/tenant.ts and server/context.ts when
they still match the starter template (or its own output); customized files
abort the generator with manual instructions before anything is written, and
--force overrides. Routes live under /api/workspaces; switching
workspaces sets the beignet-workspace cookie through a route handle
escape hatch. See Authorization.
make payments
beignet make payments
beignet db generate
beignet db migrate
beignet db statusGenerates a payments-backed billing slice for Drizzle-backed apps: a
features/billing feature with a free/pro plan model, FREE_PLAN_LIMITS
quotas, a billing-backed entitlements port, a billing_accounts table and
repository, checkout/portal/status contracts and use cases, an idempotent
app/api/webhooks/payments/route.ts webhook route, a demo billing seed, and
BILLING_PRO_PRICE_ID env validation. Local development uses the memory
payments provider; swap server/providers.ts to the Stripe provider when
credentials are configured. On apps with the frontend shell it also emits a
plan settings page at /settings/plan. Billing accounts scope to the user
until make tenancy adds workspaces, then become workspace-scoped
automatically. See Payments.
make inbox
beignet make inbox
beignet db generate
beignet db migrate
beignet db statusGenerates an in-app inbox notifications slice for Drizzle-backed apps: a
features/inbox feature with cursor-paginated list, unread-count, mark-read,
and mark-all-read contracts and use cases, an inbox_notifications table and
repository, an in-app notification channel
(defineInboxNotificationChannel), a sample notification, seeds, and tests.
The inbox is personal: rows are scoped to the signed-in user and need no
tenancy. On apps with the frontend shell it also emits an /inbox page, an
unread badge component, and a sidebar navigation entry. Add the in-app
channel to any feature notification to deliver into the inbox. See
Notifications.
db
beignet db schema sync
beignet db generate
beignet db migrate
beignet db status
beignet db seed
beignet db resetdb schema sync idempotently brings the app-owned Drizzle schema re-exports of
Beignet provider tables in sync with the installed providers, currently for
Beignet's audit, idempotency, and outbox tables. Run it before db generate
when you add those operational ports. Use --tables to sync only the table
definitions for the ports in the next migration.
The database commands (generate, migrate, status, seed, and reset) delegate to
the app-owned package script of the same name (db:generate, db:migrate,
db:status, db:seed, db:reset) and check prerequisites first — a missing
script, missing drizzle.config.*, or removed status/seed/reset entrypoint produces an error
naming the exact file to restore. The starter ships db:generate,
db:migrate, db:status, and db:reset; run db migrate first since the initial
migration is vendored, and add a db:seed script with your first feature
seeds. For generate, migrate, seed, and reset, --dry-run validates
prerequisites and reports the app-owned script without executing it; it does
not simulate the script's SQL or data changes. Status is already read-only and
always runs its inspection.
See Database.
| Option | Description |
|---|---|
--dialect sqlite|postgres|mysql | Select the schema dialect for db schema sync; otherwise inferred from server/providers.ts or drizzle.config.*. |
--tables audit,idempotency,outbox | Select which provider tables db schema sync writes; defaults to all three. |
--output <path> | Select the synced schema file for db schema sync; defaults to infra/db/schema/beignet.ts. |
--dry-run | Print the command that would run without running it; lifecycle commands do not simulate SQL or data changes. |
--json | Print the script, runner, and captured output as JSON; retains the final 64 KiB per stream and reports outputTruncated when needed. |
routes
beignet routesPrints a table of method, path, contract export, and matched Next.js handler
file for every contract the CLI can inspect. It supports contract-group
definitions and direct defineContract({ method, path }) exports. Exported
declarations and fluent calls are read from TypeScript syntax, so nested
objects, comments, and multiline expressions do not truncate contract
inspection. Local fluent-builder aliases and named local exports are followed.
Factories must be value imports from @beignet/core/contracts; named aliases
and namespace imports work, while same-named methods on unrelated objects are
ignored.
| Option | Description |
|---|---|
--json | Machine-readable route list. |
--cwd <dir> | Inspect an app root in another directory. Must point at an app, not a monorepo root. |
map
beignet map
beignet map --feature issues
beignet map --json --kind route,use-case,event,listener
beignet map --changed
beignet map --changed --base origin/main --jsonBuilds a deterministic graph from the app's source and the same facts used by
routes, lint, doctor, and provider audit. It includes features,
contracts, route groups, use cases, authorization abilities, events and their
listeners, jobs, schedules, tasks, notifications, uploads, agent capabilities,
registries, ports, app and package providers, database tables, OpenAPI
exposure, tests, cross-feature dependencies, and validation findings.
Source analysis covers TypeScript (.ts, .tsx, .mts, and .cts) and
JavaScript (.js, .jsx, .mjs, and .cjs). Statically recognizable
Beignet declarations using ES module exports become the same stable concept
nodes as their TypeScript equivalents. CommonJS require() calls still
contribute dependency evidence, so mixed-language applications do not
disappear from the graph.
The human view is a compact feature inventory. --json returns the versioned
schemaVersion: 1 graph: stable node IDs and source locations, typed edges
with source evidence and confidence, registration status, doctor/lint
diagnostics, and explicit unresolved references. It is report-only, so
findings remain visible without making the command fail.
Test nodes include JavaScript and TypeScript files named with .test or .spec
under Beignet's canonical app test roots and any relevant roots configured
through beignet.config.*.
Tests inside a feature retain feature ownership and a contains-test edge;
adjacent app, infra, server, and top-level tests remain app-level nodes. If one
of those tests imports changed source, map --changed can report it as an
exact reverse-import consumer. Treat that as impact evidence, not proof that
the test covers every behavior affected by the change.
The source graph uses TypeScript's resolver with the app's complete
tsconfig.json, or its jsconfig.json when no TypeScript config exists. It
honors baseUrl, every paths alias, multiple alias targets, JSON comments,
and extended configs. Beignet enables JavaScript resolution for this read-only
analysis without changing the app's compiler configuration. Failed local
module references remain visible as local_import_unresolved entries.
Exported direct or contract-group declarations whose method, path, or group
prefix cannot be derived statically appear as
contract_declaration_unresolved instead of disappearing. Registration
diagnostics include the stable declaration identity in subject.file and
subject.exportName, which is also what determines node registration status.
Use --changed to map a Git change set to potentially affected Beignet
concepts. Without --base, Beignet preserves staged index changes, unstaged
worktree changes, and untracked files instead of collapsing them into one net
diff against HEAD. --base <ref> also includes committed branch changes from
the merge base of that already-local ref through HEAD; the command never
fetches the ref.
The bounded result separates direct source changes, reverse local imports,
one-hop semantic consumers, related members of directly changed containers,
and feature ownership context. Governing configuration and changed direct
local workspace dependencies produce an app-wide impact. Recognized guide
files such as README.md, AGENTS.md, and CLAUDE.md, plus Markdown or text
files under a root docs/ or documentation/ directory, remain
non-impacting. Other .mdx and .txt paths remain application changes.
Static-analysis blind spots such as deleted declarations, unresolved imports,
and changed files with no mapped concept appear as explicit gaps.
Changed-file paths and gap paths are repository-relative; app-map node sources
and source evidence remain relative to the selected app root.
Changed mapping is report-only. It does not run checks or prove that a change
is correct; inspect the affected concepts and then run beignet check.
--changed cannot be combined with --feature or --kind, and --base
requires --changed.
| Option | Description |
|---|---|
--feature <name> | Keep one feature and its direct relationships. |
--kind <kinds> | Keep one or a comma-separated list of node kinds. |
--changed | Map the current Git change set to bounded, potentially affected concepts. |
--base <ref> | In changed mode, compare from the merge base of an already-local Git ref. |
--json | Print the complete graph, selected projection, or changed-impact report as machine-readable JSON. |
--cwd <dir> | Map an app root in another directory. |
explain
beignet explain feature issues
beignet explain route "POST /api/issues"
beignet explain use-case issues.create
beignet explain task issues.backfill-search
beignet explain port storage
beignet explain registry issueTasks
beignet explain table issues
beignet explain diagnostic BEIGNET_ROUTE_GROUP_UNREGISTEREDResolves any mapped concept or diagnostic against the same versioned graph as
beignet map. Kinds include features, HTTP declarations, use cases,
authorization, workflow artifacts, agent capabilities, registries, ports and
providers, tables, OpenAPI documents, entrypoints, and tests. A target may be a
stable node ID, runtime or declaration name, source selector such as
file#export, or an applicable alias such as an HTTP method/path or provider
port.
The result is deterministic and source-backed: it includes relevant nodes and
relationships, source evidence, Beignet conventions, current doctor/lint
findings, suggested files with reasons, and runnable inspection and validation
commands. Provider explanations also identify the matching entry in the
configured provider registry when the static provider audit can prove it.
Human output names the resolved app root, and each command in JSON carries its
own cwd, so --cwd explanations remain copy-pasteable. Explain never changes
the app and does not generate model-authored advice.
Feature explanations bound the relationship list and report the full versus
returned counts so large features stay useful in agent context windows. Use a
route explanation or beignet map --feature <name> --json when you need the
omitted detail.
| Option | Description |
|---|---|
--json | Print the versioned schemaVersion: 1 explanation payload. |
--cwd <dir> | Explain a concept in another app root. |
check
beignet checkRuns the whole validation loop as one command: beignet lint,
beignet doctor --strict, and the app's own lint, typecheck, and test
package scripts through the detected package manager, in that order. Every
step runs even when an earlier one fails, so a single run reports everything
that needs fixing, and the command exits non-zero when any step fails.
Missing package scripts are reported as skipped, never as failures.
Failed package-script output is bounded to the retained 64 KiB tail of each
stream, so a noisy process cannot grow the check result without limit. The
target package must declare @beignet/core, @beignet/next, or
@beignet/web as a runtime dependency. The root must also contain
app-context.ts or beignet.config.*; a canonical app with both features/
and server/index.ts is accepted when app-context.ts is missing so doctor
can report the drift. Other package roots stop before lint, doctor, or app
scripts run.
Inside a Git worktree, check fingerprints HEAD, the index, the worktree, and
non-ignored untracked files before and after validation. A changed fingerprint
makes the result stale and exits non-zero even if every step passed. Rerun
check against the resulting source. Untracked build and coverage output
excluded by Git ignore rules does not affect the fingerprint. Outside Git,
validation continues with source provenance marked unavailable; corrupt or
unreadable Git state still fails.
| Option | Description |
|---|---|
--fix | Apply doctor's low-risk fixes before capturing the provenance baseline and checking. |
--preflight | Append the disconnected runtime environment preflight after strict doctor. |
--preflight-connect | Append connected preflight with migration status and dependency health checks. Implies --preflight. |
--connect-timeout-ms <ms> | Per migration-status or dependency health check timeout for connected preflight. Defaults to 5000. |
--json | Versioned payload (schemaVersion: 1) with the step list, statuses, captured failure output, applied fixes, and starting and ending source provenance. |
--cwd <dir> | Check an app root in another directory. |
preflight
Runtime production gate, distinct from the static doctor checks: it reads
the environment the process actually runs with, so run it in the deploy
pipeline where production configuration is present.
beignet preflight
beignet preflight --connect
beignet check --preflight
beignet check --preflight-connectThe gate verifies every individually required provider env var. For mutually
exclusive credentials, one complete requiredEnvAlternatives configuration
must be present. Variant packages are scoped to the factories registered in
the app, so inactive variants do not demand credentials. Preflight also flags
values still matching .env.example (or common placeholder patterns) on
secret-like keys, validates the app env schema by importing lib/env.ts, folds
in doctor's production hardening diagnostics with promoted severities
(doctor warnings fail the gate, hints become warnings), and warns when logging
or error reporting is absent or inert. It exits 1 on any error finding.
| Flag | Meaning |
|---|---|
--connect | Inspect migration status, then boot the app server and run every port's checkHealth(). Needs network access and real credentials. |
--connect-timeout-ms <ms> | Per migration-status or dependency health check timeout. Defaults to 5000; timeout findings report the configured duration. |
--env-file <path> | Merge a dotenv-style file under the environment (existing env keys win) for local rehearsal. |
--env-module <path> | App env module validated by importing it. Defaults to lib/env.ts. |
--server-module <path> | Module exporting getServer, used by --connect. Defaults to server/index.ts. |
--json | Machine-readable output with schemaVersion: 1. |
lint
beignet lintEnforces the architecture boundaries described in
App architecture across TypeScript and JavaScript source,
including .tsx and .jsx components and the .mts, .cts, .mjs, and
.cjs module variants. It recognizes imports and re-exports, require(),
import assignments, and literal dynamic imports. Local value-import checks
follow local helper modules, so an otherwise valid use case cannot reach infra
through an intermediary. Contracts, schemas, and client roots receive an
additional client-safe graph check, including Node built-ins imported with or
without the node: prefix. Computed module references inside constrained
layers fail when lint cannot verify their destination; use a string-literal
specifier or move runtime loading behind an allowed boundary.
Resolution follows tsconfig.json, or jsconfig.json when no TypeScript
config exists, including baseUrl, paths, extended configs, and mixed
JavaScript/TypeScript dependency chains.
Every module inside a configured Beignet feature root receives a layer; allowed
feature-root helpers remain helpers, while near-miss canonical names such as
usecases/ report the expected use-cases/ path. Direct findings include the
offending file:line:column; graph-reachability findings include their complete
import chain. The command exits non-zero on any finding.
| Option | Description |
|---|---|
--json | Machine-readable diagnostics. |
--format <format> | human, json, or github workflow annotations. Defaults to human, or github when GITHUB_ACTIONS is set. |
--cwd <dir> | Lint an app root in another directory. |
doctor
beignet doctor
beignet doctor --strict
beignet doctor --fix
beignet doctor --fix --dry-run
beignet doctor --fix --plan <plan-id> --only routes.register-missingThe framework integrity report. Diagnostics cover these areas:
- Routes and contracts — contracts without handlers, handlers without contracts, query schemas without explicit transports, unregistered route groups, partially wired slices, and CRUD slices missing generated pieces.
- OpenAPI — drift in direct arrays, exported contract lists, and
contractsFromRoutes(routes)registries, plus entries for contracts outside the registered route surface. - Workflow registries — schedules and tasks missing from
server/schedules.tsorserver/tasks.ts, events with listeners and jobs missing fromdefineOutboxRegistry({...})when the app uses outbox delivery, listeners missing fromserver/listeners.tsor otherwise not referenced byregisterListeners(...), listener wiring that does not await readiness fromstart()and cleanup fromstop(), runtime manifests that omit opted-in workflow registries, and serverless footguns such as background timers in provider files, outbox draining from lifecycle hooks, and outbox registries without a drain entrypoint or without a declared and bound or deferredjobsport for registered jobs. - Errors and authorization — route-owned catalog errors missing from
features/shared/errors.ts, runtimeappError(...)calls not declared on contracts, authorization metadata without policy coverage, and audit-required metadata without audit writes or test assertions. - Database — missing Drizzle config, schema exports, scripts, or
seed/reset entrypoints, Drizzle-backed idempotency/outbox/audit ports
without table setup, provider-declared required tables missing from schema,
configured schema sources, or migrations, unwired repository adapters,
tenant-scoped Drizzle repository ports or adapters missing
TenantScopemethod arguments, rawtenantIdorworkspaceIdrepository boundaries, ortenantScopeId(scope)predicates, ports without adapters, unguarded resets, and seeds without factories or adb:seedscript. - Security and providers — devtools routes without authorization or an
explicit local-only development opt-in, cron
routes without
CRON_SECRET, installed providers without expected env configuration, Better Auth without an auth route or trusted origins, missing security headers, credentialed wildcard CORS, uploads without routes, authorization, or size limits, and notification dispatchers that bypassctx.ports.notifications. - Payments — billing slices without a payment webhook route, preferring
createPaymentWebhookRoute(...)for payment-port billing, checkout contracts missing idempotency metadata or anidempotency-keyheader, and billing webhook use cases that referencectx.ports.idempotencywithout anAppPortsidempotency declaration, billing entitlement modules that are not wired into infra providers, entitlement checks without anAppPortsentitlements declaration, plus Stripe configuration still wired to local memory payments. - Structure and versions — feature artifacts in non-canonical folders,
including normalized near-misses and known aliases such as
usecases/andevents/, with the same expected path reported by lint and doctor; strict-mode canonical conformance drift, mixed@beignet/*version ranges or installed versions, and CLI/core version skew (an informational hint suggesting the app-localbun beignet).
When production-readiness diagnostics are present, human doctor output also
prints a production hardening checklist covering secrets and provider
credentials, verified auth and tenant authority, exposed operational routes,
security headers, CORS and proxy trust, upload and storage limits, readiness
checks, worker shutdown, webhook secrets, and least-privilege provider
credentials.
Workflow artifacts the starter does not scaffold are not drift on their own;
doctor reports misplaced, unregistered, or partially wired artifacts, not
absent ones.
| Option | Description |
|---|---|
--strict | Include CI-oriented warnings (missing generated tests, conformance drift, unused route errors) and fail on warnings. Informational hints never affect the exit code. |
--fix | Apply low-risk fixes before reporting: repair generated test support; register route groups, schedules, tasks, outbox entries, listeners, Inngest jobs, and opted-in runtime-manifest entries; sync missing default Beignet provider-table exports; and repair direct OpenAPI arrays whose contracts are already imported. Registry fixes are append-only and bail out on missing or customized anchors, ambiguous imports, and partial individual registration. Database repair requires an existing schema index, default table names, a missing infra/db/schema/beignet.ts or one that still matches beignet db schema sync output, and a schema index where its generated export * can be safely added or retained. It changes source only, so run beignet db generate, beignet db migrate, and beignet db status afterward. Compatible co-located workflow repairs become one workflows.register-missing operation. |
--dry-run | With --fix, return a read-only repair plan containing stable operation IDs, SHA-256 file hashes, exact unified patches, a plan ID, the detected convention, and every current doctor diagnostic. Human and GitHub output render the diagnostics that determine the command's exit status. |
--plan <id> | With --fix, apply only if the complete current repair plan still matches this ID; otherwise fail before writing. |
--only <ids> | With --fix --plan, apply selected comma-separated operation IDs such as routes.register-missing or outbox.register-missing. |
--json | Without --dry-run, return the versioned inspection payload (schemaVersion: 1) with targetDir, config, strict, convention, contracts, routes, diagnostics, and fixes. Fix application adds the guarded planId and stable operationIds; MCP doctor_fix returns this same shape. With --fix --dry-run, return the versioned repair plan instead. |
--format <format> | human, json, or github. Defaults to human, or github when GITHUB_ACTIONS is set. --json conflicts with any other --format. |
--cwd <dir> | Check an app root in another directory. |
Architecture lint resolves the complete baseUrl and paths configuration
through TypeScript, including src/ layouts, extended configs, multiple
aliases, and fallback targets, before classifying dependency direction.
Generator registry updates report an error when a customized central array
cannot be found; “skipped” means the requested registration already exists.
provider add
beignet provider add cache-redis
beignet provider add storage-s3 --dry-run --jsonAdds a provider setup preset to an existing app. The command is idempotent:
repeated runs skip identical files and avoid duplicate provider entries. It
updates package dependencies, server/providers.ts, ports/index.ts,
infra/port-wiring.ts, .env.example, and docs/providers.md.
| Preset | Adds |
|---|---|
flags-openfeature | @beignet/provider-flags-openfeature, @openfeature/server-sdk, flags: FlagsPort, and createOpenFeatureFlagsProvider(). |
jobs-bullmq | @beignet/provider-jobs-bullmq, bullmq, jobs: JobDispatcherPort, Redis configuration, and worker setup notes. |
jobs-inngest | @beignet/provider-jobs-inngest, inngest, jobs: JobDispatcherPort, and Inngest configuration. |
mail-resend | @beignet/provider-mail-resend, resend, mailer: MailerPort, and RESEND_API_KEY/RESEND_FROM. |
mail-smtp | @beignet/provider-mail-smtp, nodemailer, mailer: MailerPort, and MAIL_* settings. |
payments-stripe | @beignet/provider-payments-stripe, stripe, payments: PaymentsPort, and Stripe API/webhook configuration. |
search-meilisearch | @beignet/provider-search-meilisearch, search: SearchPort, and MEILISEARCH_HOST. |
error-reporting-sentry | @beignet/provider-error-reporting-sentry, @sentry/node, and createSentryErrorReportingProvider(). |
rate-limit-upstash | @beignet/provider-rate-limit-upstash, Upstash peers, rateLimit: RateLimitPort, and Upstash REST env. |
cache-redis | @beignet/provider-cache-redis, ioredis, cache: CachePort, and REDIS_URL. |
event-bus-redis | @beignet/provider-event-bus-redis, ioredis, eventBus: EventBusPort, and REDIS_EVENT_BUS_URL. |
locks-redis | @beignet/provider-locks-redis, ioredis, locks: LocksPort, and REDIS_LOCKS_URL. |
storage-s3 | @beignet/provider-storage-s3, AWS S3 SDK peers, storage: StoragePort, and STORAGE_S3_BUCKET. |
storage-vercel-blob | @beignet/provider-storage-vercel-blob, @vercel/blob, storage: StoragePort, and read-write token or OIDC credentials. |
error-reporting-sentry replaces the starter no-op error reporter at provider startup without
adding errorReporter to the deferred list. The other presets add their
app-facing ports to AppPorts and defer them to provider startup. Presets that
fill the same app port, such as mail-resend and mail-smtp, fail with a
conflict instead of registering competing providers. Provider escape hatches
such as ctx.ports.redis, ctx.ports.redisEventBus,
ctx.ports.redisLocks, ctx.ports.s3Storage, ctx.ports.resend,
ctx.ports.smtp, ctx.ports.meilisearch, and ctx.ports.openFeature are
inferred from server/providers.ts.
| Option | Description |
|---|---|
--dry-run | Preview planned writes without changing files. |
--json | Versioned payload (schemaVersion: 1) with changed and skipped files plus next steps. |
--cwd <dir> | Add provider setup to an app root in another directory. |
Run your package manager install command after the preset writes dependency
changes, then run beignet provider audit and beignet doctor --strict.
provider audit
beignet provider audit
beignet provider audit --jsonReports the provider packages installed by the target app without turning the
report into a CI failure. The audit reads package-owned beignet.provider
metadata without importing provider implementation modules, then shows metadata
validity, registration status, required env, required tables, and declared app
ports in the human table. JSON output also includes active variants, provider
watchers, and source locations for matching provider-registry entries.
Missing alternative credential paths stay grouped in human diagnostics. JSON
preserves each active variant's accepted configurations and status when
multiple variants are registered.
Use the human table when checking an app manually. Use --json when a script,
CI report, or coding agent needs a machine-readable provider inventory. Stable
setup problems that should fail CI remain doctor diagnostics.
| Option | Description |
|---|---|
--json | Versioned payload (schemaVersion: 1) with targetDir, providers, and summary. |
--cwd <dir> | Audit providers for an app root in another directory. |
task run
beignet task run posts.backfill-search --tenant acme --input '{"dryRun":true}'Runs an app-owned operational task through the registry in server/tasks.ts,
which exports tasks, createTaskContext(...), and optionally
stopTaskContext(...). Keep auth, tenancy, and provider lifecycle decisions
in that module so local shells, CI jobs, and deployed runners behave the same.
| Option | Description |
|---|---|
--input <json> | JSON input validated by the task schema. Defaults to {}. |
--tenant <id|slug> | Tenant id or slug passed to the app's createTaskContext as TaskRunContextArgs.tenant, separate from task input. The app resolves it. |
--module <path> | Task registry module. Defaults to server/tasks.ts or paths.tasks. |
--cwd <dir> | Run against an app root in another directory. |
--json | Print the task result as JSON. |
schedule run
beignet schedule run posts.daily-summary --scheduled-at 2026-01-01T09:00:00.000ZRuns a schedule explicitly from a local shell, CI job, or worker through the
registry in server/schedules.ts, which exports a schedules array,
createScheduleContext(...), and optionally stopScheduleContext(...). See
Schedules.
| Option | Description |
|---|---|
--payload <json> | JSON payload for the schedule schema. Omit it to use the schedule's createPayload(...). |
--module <path> | Schedule registry module. Defaults to server/schedules.ts or paths.schedules. |
--run-id <id> | Provider or app schedule run ID. |
--attempt <number> | One-based provider attempt number. |
--scheduled-at <date> | Provider scheduled timestamp. |
--triggered-at <date> | Schedule trigger timestamp. |
--source <label> | Provider or app source label. |
--cwd <dir> | Run against an app root in another directory. |
--json | Print the run result as JSON. |
outbox drain
beignet outbox drain --batch-size 100 --concurrency 4Drains durable events and jobs in one bounded pass through the registry in
server/outbox.ts, which exports outboxRegistry,
createOutboxDrainContext(...), and optionally stopOutboxDrainContext(...).
There is intentionally no separate jobs-drain command: outbox-backed jobs
drain here, and direct provider jobs use provider-owned worker entrypoints
such as an Inngest route. See Outbox.
Every outbox command accepts --cwd <dir> to run against an app root in
another directory.
| Option | Description |
|---|---|
--batch-size <number> | Maximum eligible messages to handle in one pass. |
--concurrency <number> | Maximum concurrent deliveries. Values above one do not preserve order. |
--module <path> | Outbox registry module. Defaults to server/outbox.ts or paths.outbox. |
--json | Print the drain result as JSON. |
The JSON result includes settlement and lease uncertainty counters. If
settlementFailed or leaseLost is nonzero, the command still prints the
complete report and exits 1 so automation cannot mistake an unknown durable
state for success.
outbox list
beignet outbox list --status deadLetteredLists messages through ports.outboxAdmin from the app's outbox context. If
the context does not expose outboxAdmin, the CLI accepts ports.outbox only
when it also implements OutboxAdminPort, such as the memory outbox in tests.
| Option | Description |
|---|---|
--status <status> | pending, claimed, delivered, or deadLettered. |
--kind <kind> | event or job. |
--name <name> | Event or job name. |
--limit <number> | Maximum messages to return. Defaults to 50. |
--module <path> | Outbox registry module. Defaults to server/outbox.ts or paths.outbox. |
--json | Print messages and total count as JSON. |
outbox show
beignet outbox show <message-id>Shows one outbox message, including payload, attempts, timestamps, and last
error. Use --json for the full machine-readable record.
outbox requeue
beignet outbox requeue <message-id> --reset-attemptsReturns one dead-lettered message to pending state. Requeue only after the
handler or provider issue has been fixed; Beignet preserves the last error for
inspection.
| Option | Description |
|---|---|
--available-at <date> | Earliest timestamp the message may be claimed again. Defaults to now. |
--reset-attempts | Reset attempts to zero before requeueing. |
--module <path> | Outbox registry module. Defaults to server/outbox.ts or paths.outbox. |
--json | Print the requeued message as JSON. |
outbox purge
beignet outbox purge --before 2026-01-01T00:00:00.000Z --dry-run
beignet outbox purge --before 2026-01-01T00:00:00.000ZDeletes dead-lettered messages whose updatedAt is before the cutoff. The
command requires either --before or --all.
| Option | Description |
|---|---|
--before <date> | Only purge dead-lettered messages last updated before this timestamp. |
--all | Purge every dead-lettered message when --before is omitted. |
--limit <number> | Maximum messages to purge, deleting the oldest eligible rows first. |
--dry-run | Count matches without deleting rows. |
--module <path> | Outbox registry module. Defaults to server/outbox.ts or paths.outbox. |
--json | Print matched/deleted counts as JSON. |
outbox prune
beignet outbox prune --before 2026-01-01T00:00:00.000Z --dry-run
beignet outbox prune --before 2026-01-01T00:00:00.000ZDeletes delivered messages whose deliveredAt is before the cutoff.
| Option | Description |
|---|---|
--before <date> | Required delivered-row retention cutoff. |
--limit <number> | Maximum messages to prune, deleting the oldest eligible rows first. |
--dry-run | Count matches without deleting rows. |
--module <path> | Outbox registry module. Defaults to server/outbox.ts or paths.outbox. |
--json | Print matched/deleted counts as JSON. |
Programmatic orchestrators can import runAppTask, runAppSchedule,
runOutboxDrain, runOutboxList, runOutboxShow, runOutboxRequeue,
runOutboxPurge, and runOutboxPrune plus their option and result types from
@beignet/cli. Each function returns the same versioned report shape as its
CLI --json command and operational MCP tool. Programmatic task input and
schedule payloads accept any pre-parsed value supported by the registered
Standard Schema, including raw strings. The CLI --input and --payload
flags decode JSON before calling these APIs.
mcp
beignet mcpRuns a Model Context Protocol server over stdio so coding agents can read
app-local guidance and focused feature app-map resources, then call the CLI as
tools: app_map, explain, check, routes, doctor, db, db_status,
db_schema_sync, task_run, schedule_run, outbox_inspect, outbox_run,
doctor_fix_plan, doctor_fix, lint, make, and provider_add.
doctor_fix_plan is read-only and returns the same guarded repair plan as
beignet doctor --fix --dry-run --json; pass its planId and optional
fixIds to doctor_fix, or omit both to preserve apply-all behavior.
app_map accepts optional feature,
kinds, and includeDiagnostics inputs so an agent can inspect the whole app
or request a token-bounded projection before editing. Pass
{ changed: true, base?: "origin/main" } for the same bounded report as
beignet map --changed --json. Changed mode rejects projection and diagnostic
inputs. explain accepts
{ kind, target } for any mapped concept or diagnostic and returns
the same versioned result as beignet explain <kind> <target> --json. check
accepts optional
{ preflight?: boolean, preflightConnect?: boolean, connectTimeoutMs?: number, timeoutMs?: number }.
Connected preflight adds migration status and dependency health checks. Check
bounds failure output and cancels its active package script when the MCP request
is cancelled. It does not apply Beignet fixes, but app-owned scripts run with
their normal side effects, so MCP clients should treat it as an execution tool
rather than a read-only inspection tool. It returns the same versioned result
as beignet check --json, including starting and ending source provenance. An
ending fingerprint that differs from the baseline makes the result stale and
unsuccessful. db accepts
{ command: "generate" | "migrate" | "seed" | "reset", dryRun?, timeoutMs? },
returns the matching versioned lifecycle report with bounded output, and stops
the complete child process tree on cancellation or timeout. db_status is
read-only and returns the same current, pending, or failed report as
beignet db status --json; pending status exits 2. db_schema_sync
accepts { dialect?, tables?, output?, dryRun? } and returns the same report
as beignet db schema sync --json; it is idempotent but writes app source
unless dry-run is selected. task_run and schedule_run execute registered
app workflows. outbox_inspect keeps list and show read-only, while
outbox_run owns drain, requeue, purge, and prune; purge and prune
support dryRun. These operational tools run app code in isolated process
trees, bound structured results, and stop on request cancellation or timeout.
Their optional module overrides are app-relative and cannot leave the app
root where the MCP server started. Cancellation and timeouts cannot roll back
side effects that already completed. Inspect app state before retrying an
interrupted operation or one that reports a cleanup failure.
The server publishes beignet://app/guidance and the
beignet://app/features/{feature} resource template. Tool outputs are the
same JSON the matching --json flags print, make takes the same artifact
kinds as beignet make, and provider_add takes { preset, dryRun? } for
the same presets as beignet provider add. Generated apps ship a .mcp.json
that runs
./node_modules/.bin/beignet mcp, so MCP clients such as Claude Code pick up
the app-local CLI version without configuration. See
Coding agents for the tool list, manual client registration, and
the rest of the agent surface.
completion
beignet completion install
beignet completion install --shell zsh
beignet completion uninstallinstall writes a managed completion block to ~/.bashrc or ~/.zshrc,
detecting the shell from $SHELL; uninstall removes it. Restart your shell
or source the rc file to activate. Completions cover commands, subcommands,
flags, and enum values such as --with and --format, and complete whatever
beignet resolves to on your PATH through the internal
beignet completion propose helper.
| Option | Description |
|---|---|
--shell <shell> | bash or zsh. Defaults to $SHELL. |
--json | Print the install or uninstall result as JSON. |
Exit codes
Every command uses the same exit code contract, so CI scripts can branch on the result:
| Code | Meaning |
|---|---|
0 | Success. lint and doctor found nothing to report. |
1 | Findings. lint or doctor reported problems, or a command failed against the app. |
2 | Usage or internal error, such as an unknown command, invalid flags, or an unexpected CLI failure. |