npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@beignet/cli

v0.0.56

Published

CLI for creating and maintaining Beignet apps.

Readme

@beignet/cli

[!CAUTION] Beignet is experimental alpha software. The 0.0.x package line is for early evaluation, and APIs may change between releases while the framework settles.

Command-line tools for creating and maintaining Beignet apps.

Beignet requires Node.js 22.12 or newer. Bun is optional; npm, pnpm, Yarn, and Bun can run CLI commands.

The package ships a single beignet bin. Generated apps install @beignet/cli as a dev dependency and add a beignet package script, so the day-to-day form inside an app is:

bun beignet check

With npm use npm run beignet -- check, with pnpm pnpm beignet check, and with yarn yarn beignet check. For one-off runs outside an app, invoke the scoped package directly:

bunx @beignet/cli routes

Never run the unscoped beignet name through bunx or npx; that npm name belongs to an unrelated package.

Create an app

App scaffolding lives in the create-beignet package:

npm create beignet@latest my-app
# or
bun create beignet my-app

Both delegate to beignet create, so bunx @beignet/cli create my-app is equivalent.

Running create in an interactive terminal without selection flags opens a prompt-based setup that asks for the project directory, whether to scaffold an API-only app, which database to use, and which providers to add. Passing any selection flag (--api, --db, --providers, or --package-manager) skips the prompts, and --yes forces the non-interactive defaults (the full-stack SQLite starter with bun) even on a terminal. Non-interactive runs in scripts and CI behave exactly as before.

By default the CLI creates an app using Next.js 16.3.3 or newer within v16, with:

  • Contract-first todos API routes
  • Better Auth routes and Beignet auth provider wiring
  • Drizzle persistence — SQLite (libSQL) by default, with --db postgres and --db mysql for the other backends
  • UOW, audit logging, and durable idempotency
  • App-owned ports with provider-backed infra adapters
  • No-op error reporting wired through the HTTP error reporting hook
  • Validated application use cases
  • A Beignet server wired through Next.js route handlers
  • App error helpers, auth helpers, env validation, health checks, and devtools
  • AGENTS.md and CLAUDE.md agent guides, plus a .mcp.json that registers the beignet mcp server for MCP clients such as Claude Code
  • A typed client
  • TanStack Query
  • React Hook Form
  • OpenAPI output

Workflow-tier pieces such as events, jobs, listeners, schedules, seeds, tasks, uploads, and the outbox are added by beignet make ... generators when you opt into those capabilities.

Pass --api to scaffold an API-only app. It keeps the same contracts, auth, persistence, and devtools, and swaps the UI shell for a minimal API landing page and a reduced typed client.

The starter ships local production-shaped defaults: Better Auth, Drizzle persistence, pino logging, UOW, durable idempotency, audit logging, no-op error reporting, and devtools. Add workflow artifacts and external service providers only when you want those capabilities:

bunx @beignet/cli create my-app --providers jobs-inngest,mail-resend

Available starter-native providers:

  • jobs-inngest - Inngest-backed background jobs via @beignet/provider-jobs-inngest
  • mail-resend - Resend-backed mail via @beignet/provider-mail-resend
  • rate-limit-upstash - Upstash-backed rate limiting via @beignet/provider-rate-limit-upstash

--providers accepts every beignet provider add preset (flags-openfeature, jobs-bullmq, jobs-inngest, mail-resend, mail-smtp, payments-stripe, cache-redis, event-bus-redis, locks-redis, storage-s3, search-meilisearch, error-reporting-sentry, and storage-vercel-blob). Presets are applied to the fresh scaffold with the same machinery as beignet provider add; starter-native presets are rendered directly, and selections that fill the same app port (for example mail-resend plus mail-smtp) fail before any files are written.

Each selected provider adds its package dependencies, wires the provider in server/providers.ts, extends .env.example, and writes setup notes to docs/providers.md.

Drizzle persistence ships in every starter; pick the backend with --db (default sqlite):

bunx @beignet/cli create my-app --db postgres

--db accepts sqlite (libSQL, local files plus hosted libSQL/Turso), postgres (node-postgres, requires a running Postgres 14+ server), and mysql (mysql2, requires MySQL 8.0+). Every backend scaffolds the same structure: infra/db/schema/, infra/db/repositories.ts, infra/db/test-database.ts, SQL-backed idempotency and outbox tables, a Drizzle todo repository adapter, drizzle.config.ts with the matching dialect, checked-in starter migrations, database scripts, and env examples (SQLITE_*, POSTGRES_DB_URL, or MYSQL_DB_URL), so the generated todo resource uses durable persistence instead of an in-memory repository. Postgres starters run repository tests against in-process PGlite with no server; MySQL persistence tests need a real server via MYSQL_TEST_URL. Later make feature or make resource calls detect the app's backend from infra/db/repositories.ts and generate a dialect-correct Drizzle table/repository plus an in-memory test fake.

Better Auth also ships in every starter. The scaffold adds the Better Auth Next.js route, Beignet auth provider wiring, typed UNAUTHORIZED and FORBIDDEN errors, a request-bound authorization gate, and requireUser(ctx). Use hooks for HTTP boundary authentication and use cases or policies for business authorization.

Preview the planned files without writing anything:

bunx @beignet/cli create my-app --dry-run
bunx @beignet/cli create my-app --dry-run --json

When --providers includes post-create presets, the preview composes their dependency, wiring, environment, and setup-note changes into the final file list without creating the target directory.

The CLI writes files only. After creation:

cd my-app
bun install
cp .env.example .env.local
bun beignet db migrate
bun beignet db status
bun run dev

Open http://localhost:3000 for the starter UI. The generated homepage links to the todo feature, health check, OpenAPI document, devtools, and Beignet docs.

Generated apps also trust Beignet's package-shipped TanStack Intent skills via package.json#intent.skills. Run bunx @tanstack/intent@latest install (or the matching runner for your package manager) to add Intent's managed guidance block, then load matching skills such as @beignet/core#app-architecture, @beignet/next#routes-server, @beignet/web#fetch-server, @beignet/devtools#runtime-safety, @beignet/provider-db-drizzle#database-provider, @beignet/provider-auth-better-auth#auth-provider, @beignet/react-query#client, @beignet/react-hook-form#forms, and @beignet/cli#app-structure before substantial Beignet changes. The CLI skill covers generators, full-slice recipes, db schema sync, lint, doctor, and MCP tooling.

Inspect the app, then generate the next feature:

# in another terminal, from my-app
bun beignet routes
bun beignet map
bun run lint
bun beignet lint
bun beignet doctor
bun beignet make feature projects
bun beignet db generate
bun beignet db migrate
bun beignet db status
bun run test
bun run lint
bun run typecheck
bun beignet lint
bun beignet doctor

Options

beignet create [directory] [options]
beignet task run <name> [options] [--cwd <dir>]
beignet outbox drain [options] [--cwd <dir>]
beignet outbox list [options] [--cwd <dir>]
beignet outbox show <id> [options] [--cwd <dir>]
beignet outbox requeue <id> [options] [--cwd <dir>]
beignet outbox purge [options] [--cwd <dir>]
beignet outbox prune [options] [--cwd <dir>]
beignet schedule run <name> [options] [--cwd <dir>]
beignet make task <feature.name> [options] [--cwd <dir>]
beignet make contract <name> [options] [--cwd <dir>]
beignet make event <feature.name> [options] [--cwd <dir>]
beignet make factory <feature.name> [options] [--cwd <dir>]
beignet make feature <name> [options] [--cwd <dir>]
beignet make job <feature.name> [options] [--cwd <dir>]
beignet make notification <feature.name> [options] [--cwd <dir>]
beignet make inbox [options] [--cwd <dir>]
beignet make outbox [options] [--cwd <dir>]
beignet make payments [options] [--cwd <dir>]
beignet make tenancy [options] [--cwd <dir>]
beignet make listener <feature.name> --event <feature.event> [options] [--cwd <dir>]
beignet make schedule <feature.name> [options] [--cwd <dir>]
beignet make upload <feature.name> [--ui] [options] [--cwd <dir>]
beignet make port <name> [options] [--cwd <dir>]
beignet make policy <name> [options] [--cwd <dir>]
beignet make adapter <name> [options] [--cwd <dir>]
beignet make resource <name> [options] [--cwd <dir>]
beignet make seed <feature.name> [options] [--cwd <dir>]
beignet make test <feature.action> [options] [--cwd <dir>]
beignet make use-case <feature.action> [options] [--cwd <dir>]
beignet db generate [--dry-run] [--json] [--cwd <dir>]
beignet db migrate [--dry-run] [--json] [--cwd <dir>]
beignet db status [--json] [--cwd <dir>]
beignet db seed [--dry-run] [--json] [--cwd <dir>]
beignet db reset [--dry-run] [--json] [--cwd <dir>]
beignet db schema sync [--dialect sqlite|postgres|mysql] [--tables audit,idempotency,outbox] [--output <path>] [--dry-run] [--json] [--cwd <dir>]
beignet routes [--json] [--cwd <dir>]
beignet map [--json] [--feature <name>] [--kind <kinds>] [--changed] [--base <ref>] [--cwd <dir>]
beignet explain <kind> <target> [--json] [--cwd <dir>]
beignet check [--json] [--fix] [--preflight] [--preflight-connect] [--connect-timeout-ms <ms>] [--cwd <dir>]
beignet preflight [--connect] [--connect-timeout-ms <ms>] [--json] [--cwd <dir>]
beignet lint [--json] [--cwd <dir>] [--format human|json|github]
beignet doctor [--json] [--strict] [--fix] [--dry-run] [--plan <id>] [--only <operation-ids>] [--cwd <dir>] [--format human|json|github]
beignet provider add <preset> [--dry-run] [--json] [--cwd <dir>]
beignet provider audit [--json] [--cwd <dir>]
beignet mcp
beignet completion install [--shell bash|zsh] [--json]
beignet completion uninstall [--shell bash|zsh] [--json]
beignet --version

Options:
  --template next              Template to use. Currently only `next`.
  --api                       Scaffold an API-only app without the UI shell.
  --db postgres               Database backend: sqlite (default), postgres, or mysql.
  --package-manager bun       Package manager shown in next steps.
  --providers jobs-inngest,mail-resend  Add provider presets. Accepts one value or a comma-separated list.
  --yes                       Skip interactive create prompts and use the defaults.
  --force                     Write into a non-empty directory.
  --dry-run                   Preview create/make writes without changing files.
  --json                      Print machine-readable output.
  --cwd path/to/app           Run an existing-app command against another app directory.
  --preflight-connect         Add connected migration and dependency checks.
  --connect-timeout-ms 5000   Override the connected preflight per-check timeout.
  --format github             Output format for lint and doctor: human, json, or github.
  --input '{"dryRun":true}'   JSON input for `beignet task run`.
  --tenant acme               Tenant id or slug passed to the app's createTaskContext by `beignet task run`, separate from task input.
  --payload '{"date":"..."}'  JSON payload for `beignet schedule run`.
  --module server/tasks.ts    Override the task, outbox, or schedule registry path.
  --batch-size 100            Maximum outbox messages to claim in one drain pass.
  --run-id run_123            Provider or app schedule run ID for schedule runs.
  --scheduled-at 2026-01-01   Provider scheduled timestamp for schedule runs.
  --triggered-at 2026-01-01   Trigger timestamp for schedule runs.
  --attempt 1                 One-based provider attempt number for schedule runs.
  --source manual             Provider or app source label for schedule runs.
  --with policy,event         Add optional artifacts to `make feature`; supports policy, factory, seed, task, event, listener, job, notification, schedule, ui, and upload.
  --recipe full-slice         Add the canonical full-slice feature recipe: policy, client UI, workflow artifacts, outbox-ready events/jobs, and listener registration.
  make tenancy                Add `features/workspaces` with membership-aware tenant resolution, workspace/member/invite routes, a demo seed, and workspace settings UI on shell apps.
  make payments               Add `features/billing` with free/pro plans, a `createPaymentWebhookRoute(...)` route, billing persistence, entitlement gating, a demo seed, and a plan settings UI on shell apps.
  make inbox                  Add `features/inbox` with a per-user notification inbox, an in-app notification channel, cursor-paginated routes, and an inbox page on shell apps.
  --authorization             Add authorization metadata, policy wiring, policy tests, and use-case gate checks to `make resource`.
  --tenant-scoped             Add tenant-scoped schemas, `TenantScope` repository boundaries, and use-case tenant checks to `make resource`.
  --events                    Add created, updated, and deleted domain events to `make resource`.
  --soft-delete               Archive resource rows with deletedAt instead of hard-deleting them.
  --event posts.published     Event for `make listener`.
  --cron "0 9 * * *"          Cron expression for `make schedule`.
  --timezone America/Chicago  Timezone for `make schedule`.
  --route                     Add a Next.js cron route for `make schedule`.
  --ui                        Add a typed upload client, React uploader, and component test to `make upload`.
  provider add cache-redis   Add dependencies, provider wiring, env examples, and setup notes for a supported provider preset.
  --strict                    Include CI-oriented doctor warnings and fail on warnings.
  --fix                       Apply low-risk doctor fixes before reporting.
  --dry-run                   Preview an exact doctor fix plan without writing files.
  --plan id                   Apply only when the current repair plan matches this id.
  --only routes.register-missing  Apply selected comma-separated repair operations. Requires --plan.
  --shell zsh                 Shell for `completion install`/`uninstall`. Defaults to $SHELL.
  -h, --help                  Show help.
  -v, --version               Print the CLI version.

Exit codes

Every command uses the same exit-code matrix:

| Code | Meaning | | ---- | ------------------------------------------------------------------------ | | 0 | Success. | | 1 | Findings: lint diagnostics, doctor errors, or strict-mode doctor warnings. | | 2 | Usage or internal errors: unknown commands, bad flags, missing arguments, runtime failures, or running beignet with no arguments. |

--format never changes the exit code; lint --format github still exits 1 when it finds diagnostics.

Output formats

lint and doctor support three output formats. --json stays the canonical machine-readable flag; --format adds GitHub Actions annotations:

beignet lint --format github
beignet doctor --strict --format github

The github format emits one ::error, ::warning, or ::notice workflow command per diagnostic, with file, line, and col properties when known. When GITHUB_ACTIONS=true is set and no format is passed, lint and doctor default to the github format; otherwise they default to human output. Combining --json with a conflicting --format is an error. Human output groups doctor diagnostics by severity, colors severity labels on TTYs, and respects NO_COLOR.

All --json results begin with schemaVersion: 1 so other tools can detect output-shape changes.

Shell completions

Install tab completions for the beignet bin into your shell rc file:

beignet completion install
beignet completion install --shell zsh
beignet completion uninstall

install writes a managed block to ~/.bashrc or ~/.zshrc (detected from $SHELL when --shell is omitted) and uninstall removes it. Restart your shell or source the rc file to activate completions. Zsh completions require compinit to be loaded, which most zsh setups already do.

Completions cover commands, subcommands, flags, and enum flag values such as --template and --providers. The shell scripts call the beignet completion propose helper, which prints one proposal per line for a partial command line; it exists for the shell integration and is safe to ignore otherwise. Completions complete whatever beignet resolves to on your PATH, such as the app-local bin installed by generated apps.

Beignet CLI commands are convention-aware. routes, map, lint, doctor, and generators work best in the Next.js layout created by beignet create:

features/
app/api/
server/context.ts
server/index.ts
server/routes.ts

server/context.ts declares the app's context blueprint with defineServerContext(...); server/index.ts passes it to the server and generated route tests pass the same value to createTestApp(...).

Route inspection supports contract-group definitions and direct defineContract({ method, path }) exports. It reads exported declarations and fluent contract calls from TypeScript syntax, so nested objects, comments, and multiline expressions do not truncate a contract before its method or path. 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.

The standard app layout includes the files feature generators expect:

app-context.ts
infra/port-wiring.ts
ports/index.ts
lib/routes.ts
lib/use-case.ts

Workflow builders are created on demand. New apps do not ship lib/jobs.ts, lib/listeners.ts, lib/notifications.ts, lib/schedules.ts, or lib/tasks.ts; each beignet make ... workflow generator creates only the app-bound builder it needs.

The CLI uses this shape to inspect routes, wire generated features, and report drift safely. beignet doctor treats absent workflow-tier files as fine and reports misplaced or stale workflow artifacts when they drift from the app layout.

Use beignet.config.ts, beignet.config.js, beignet.config.mjs, or beignet.config.json to select the server adapter profile or when your app keeps the same architecture under different paths. "next" is the default; use "web" when the server exposes createFetchServer(...) from @beignet/web:

import { defineConfig } from "@beignet/cli/config";

export default defineConfig({
	framework: "web",
});

The web profile derives route coverage from canonical feature route groups in the central defineRoutes(...) registry passed to createFetchServer(...), so routes and doctor do not require Next.js app/api/ files. Vite and Bun do not need separate values: they are client tooling and a Fetch host, respectively.

Path overrides remain available for either profile:

// beignet.config.ts
import { defineConfig } from "@beignet/cli/config";

export default defineConfig({
	framework: "next",
	paths: {
		appContext: "src/app-context.ts",
		contracts: "src/features",
		features: "src/features",
		ports: "src/ports/index.ts",
		portWiring: "src/infra/port-wiring.ts",
		routesBuilder: "src/lib/routes.ts",
		routes: "src/app/api",
		server: "src/core/server/index.ts",
		listeners: "src/core/server/listeners.ts",
		tasks: "src/core/server/tasks.ts",
		outbox: "src/core/server/outbox.ts",
		schedules: "src/core/server/schedules.ts",
	},
});

Config values are optional overrides. Omitted paths fall back to the generated defaults. routes, lint, doctor, and make generators all load the same resolved config before inspecting or writing files. When appContext is moved under a source root such as src/app-context.ts, client setup follows that root at src/client/index.ts.

Framework-neutral generators work in both profiles. make payments, make upload, make outbox, and make schedule --route currently generate Next.js App Router handlers and therefore reject framework: "web" before writing. The create command also remains a Next.js scaffold.

The generated lib/beignet-test-runner.ts also resolves all four supported config formats and includes configured test, feature, route, server, port, infra-port, route-builder, and use-case-builder roots in test discovery. The generated test script invokes this runner with node --import tsx, so the Node test suite uses the same runtime even when the package script is started with Bun, npm, pnpm, or Yarn. beignet doctor --fix upgrades older generated bun test and tsx lib/beignet-test-runner.ts scripts without replacing custom test commands.

The same config can declare app-owned names for Beignet operational database tables when an app uses names other than the provider defaults:

// beignet.config.ts
import { defineConfig } from "@beignet/cli/config";

export default defineConfig({
	database: {
		tables: {
			audit: "audit_events",
			idempotency: "idempotency_records",
			outbox: "outbox_messages",
		},
		schemaSources: ["@acme/db/schema"],
	},
});

doctor uses these names when checking Drizzle-backed audit, idempotency, and outbox wiring. Runtime code still needs the matching provider tableName option wherever the Drizzle setup statements and ports are created.

Use database.schemaSources when operational table definitions live outside the app's canonical infra/db/schema/, drizzle/, or infra/db/ files. Each entry can be an app-relative file or directory, an @/ app path, or a package specifier such as @acme/db/schema. doctor and provider audit also follow schema-like imports from app database files, but explicit schema sources make shared-package layouts predictable.

Provider metadata describes the standard environment requirements checked by doctor --strict and provider audit. When an injected client gets its configuration from a platform binding, IAM, or another non-env mechanism, declare only the irrelevant static requirement as an explicit exception:

import { defineConfig } from "@beignet/cli/config";

export default defineConfig({
	providerAudit: {
		ignoreRequiredEnv: ["POSTGRES_DB_URL"],
	},
});

This affects CLI provider auditing only. It does not skip createEnv(...) or provider runtime configuration validation.

Workspace source packages

A web app and a worker can share Beignet application code and infrastructure through ordinary workspace packages. Each runtime owns startup and shutdown; shared packages export contracts, use cases, port types, and adapter factories. Sharing an adapter implementation does not share an in-memory instance across processes. Use the same persistent database or service when runtimes need shared state.

Opt into source analysis from the app's beignet.config.ts:

import { defineConfig } from "@beignet/cli/config";

export default defineConfig({
  framework: "web",
  workspace: {
    packages: ["../../packages/application", "../../packages/infra"],
  },
});

List every source package to inspect, including transitive dependencies. Paths are relative to the app and may point to sibling packages. Each package needs a unique package.json name and must be reachable through declared dependencies from the app. Package roots cannot overlap. Beignet does not discover or inspect all of node_modules; install workspace dependencies using your package manager. Turborepo can schedule the package scripts but is not required by Beignet.

Each package uses its own beignet.config.* paths and tsconfig.json (or jsconfig.json). An application package can keep the usual features/, ports/, and lib/ layout. An infra package containing adapters under src/ can declare:

// packages/infra/beignet.config.ts
import { defineConfig } from "@beignet/cli/config";

export default defineConfig({
  paths: { portWiring: "src/port-wiring.ts" },
});

This identifies src/ as the package's infrastructure layer. A package that only exports adapter factories does not need a port-wiring file. The consuming app can compose those factories in its own infra/port-wiring.ts.

Expose source entrypoints through package exports, for example:

{
  "name": "@example/infra",
  "private": true,
  "type": "module",
  "exports": { "./notes": "./src/notes.ts" },
  "dependencies": { "@example/application": "workspace:*" }
}

The normal TypeScript resolver handles exports, subpaths, conditions, package imports, and configured aliases. Configure the consuming build and runtime to resolve the same source. Beignet does not rewrite imports or redirect built exports to guessed source files. Missing exports and entries that resolve only to excluded build output produce BEIGNET_WORKSPACE_IMPORT diagnostics.

beignet lint follows imports and reexports across included packages and applies each owner's architecture rules. Moving an adapter to a package does not make it accessible to use cases or browser code. Cross-package imports require a dependency declaration in the importing package, including type-only imports. Relative imports, TypeScript aliases, and package.json#imports aliases that resolve outside the configured source roots also produce a workspace diagnostic; include the owning source package in workspace.packages. Aliases to installed external dependencies remain external imports.

App-owned provider checks recognize reexported AppPorts declarations and provider metadata in hoisted node_modules installations. Inferred declarations such as AppPorts = typeof initialPorts follow the underlying wiring declaration through reexport aliases. Barrels may import a symbol and then export it, including type-only exports of shared port interfaces.

beignet routes and doctor inspect shared contracts and explicit defineRoutes(...) / defineRouteGroup(...) composition from the selected server. Inspection follows the exported server or the server returned by an exported getServer = createNextServerLoader(...). Unrelated server factories do not register routes, and verified per-file Next.js handlers remain valid without central registration. Contract matching follows imports and local aliases to the declaration, so shared export names and loader-local variables remain distinct. Computed or ambiguous workspace route registration produces BEIGNET_WORKSPACE_ROUTES_UNRESOLVED. map and explain include shared source and qualify shared feature names, for example @example/application:notes. Evidence paths remain relative to the selected app and can contain ../. map --changed includes changes to configured source packages.

Keep lifecycle provider registration, environment configuration, database commands, and operational workflow registries app-owned. Provider and operational doctor checks still use those app conventions; this source analysis does not make arbitrary imported runtime factories statically verifiable. Worker-only packages can use lint to check their dependencies; the HTTP-focused routes and doctor commands run against the web app.

Generators and doctor repairs remain scoped to the selected app. paths.* values must still stay inside that app; workspace.packages grants read-only analysis, not permission to generate or repair files in another package. The standard single-app examples remain the default architecture.

Generate a contract

Use make contract when you want to start with the HTTP contract only:

beignet make contract projects

The command honors beignet.config.* path overrides and writes features/projects/contracts.ts. It creates a self-contained contract group with a starter list endpoint, schema, standard error response, and exported contract list. It does not wire route handlers, use cases, ports, or OpenAPI; use make feature when you want the full path from contract to tests.

Like make feature, make contract skips identical files and stops on divergent files unless you pass --force.

Generate a feature

Inside a Beignet app, scaffold the standard contract-first vertical slice for a product capability or workflow with:

beignet make feature projects

The command honors beignet.config.* path overrides. It writes:

  • features/projects/contracts.ts
  • features/projects/use-cases.ts
  • features/projects/ports.ts
  • infra/projects/drizzle-project-repository.ts
  • features/projects/routes.ts
  • features/projects/tests/projects.test.ts

It also wires the slice into the central route registry (server/routes.ts in generated apps), ports/index.ts, infra/db/repositories.ts, and adds a test script if the app does not already have one. The generated slice uses a small name field by default so the app stays typecheckable immediately. Treat that field as a placeholder and shape the schemas, use cases, repository, and tests around the feature's real workflow.

Add common feature-owned artifacts with --with:

beignet make feature projects --with policy,event,job,notification,ui,upload

The optional artifacts use predictable starter names:

  • features/projects/policy.ts
  • features/projects/domain/events/created.ts
  • features/projects/jobs/process.ts
  • features/projects/notifications/created.ts
  • features/projects/components/projects-panel.tsx
  • features/projects/attachment-upload-manifest.ts
  • features/projects/client/attachment-upload.ts
  • features/projects/components/attachment-uploader.tsx
  • features/projects/uploads/attachment.ts

Event, job, notification, and upload registries are created or updated in the matching folder index.ts files. The UI addon is opt-in:

beignet make feature projects --with ui

It writes a React component that imports the shared rq adapter and feature contracts, then passes query/mutation options directly to TanStack Query. The component binds the create contract through rhf(contract), reuses its body schema for field validation, maps server failures with rootFormError(...), resets after success, and refreshes the generated list query through mutationOptions({ invalidates }). If the app does not have one yet, the generator also creates the canonical client/forms.ts adapter and installs the React Hook Form dependencies.

The addon targets React Hook Form. In an app that depends on @beignet/react-form, both --with ui and --recipe full-slice stop with guidance. Generate the feature without those options, then write its component using the TanStack Form guide. You can still select other addons with --with.

Use the named full-slice recipe when you want a richer reference slice:

beignet make feature projects --recipe full-slice

The recipe includes the standard contract, schema, use-case, port, route, test, repository, and wiring files, then adds policy, factories, seeds, tasks, events, listeners, jobs, notifications, schedules, components, an upload definition with a connected uploader, outbox wiring, and outbox_messages Drizzle schema. The generated create use case publishes the generated created event so the event artifact is connected, and the listener registry is registered in server/listeners.ts and wired from server/providers.ts. Replace the starter names, listener/job bodies, notification payloads, and task behavior with the app's real workflow. For workflows that require durable audit records, record through the app audit port inside the same Unit of Work transaction as the write.

Generate a use case

Use make use-case inside a Beignet app when you want a focused application workflow without generating HTTP contracts or ports:

beignet make use-case projects.archive-project

The name uses feature.action format. For a new feature, the command creates features/projects/use-cases.ts. For an existing feature, it appends to that module or adds use-cases/archive-project.ts and updates use-cases/index.ts. Compact appends preserve handwritten code; declaration/import collisions stop generation even with --force. Dry runs validate and report edits without writing.

Use --useCaseLayout split with make feature, make resource, or make use-case to select separate files for a new feature. Existing layouts are preserved; selecting a different layout requires moving the files manually. Do not keep both use-cases.ts and use-cases/ in one feature. The inbox, tenancy, and payments recipes use split modules.

Read-style actions that start with get, list, find, search, or count generate .query(...); other actions generate .command(...).

Generate a test

Use make test after generating a focused use case. Generated features and resources already include their baseline contract, use-case, route, policy, and repository-oriented tests; make test is for adding coverage around an additional workflow that does not need a whole new resource slice.

beignet make use-case projects.archive-project
beignet make test projects.archive-project

The command writes features/projects/tests/archive-project.test.ts, builds the app context through createTestPorts(...), createTestContextFactory(...), and createUseCaseTester(...), and asserts the starter { ok: true } response. It also adds a test script when the app does not already define one. Treat the output as a compiling starting point: replace the input, context setup, and assertion with behavior-specific coverage as the use case grows.

Generate a port

Use make port inside a Beignet app when a use case needs a new application boundary:

beignet make port email

The command writes ports/email.ts, adds the port to AppPorts, creates a small fake adapter for tests, and wires a throwing infra stub so the app still typechecks until you replace it with a real port adapter. The generated port starts with a generic execute method; rename it to the domain operation your use case needs.

Generate a policy

Use make policy when repeated authorization rules need a named home:

beignet make policy posts

The command writes features/posts/policy.ts with a definePolicy(...) starter. Register the policy with createGate(...), install the gate as a port, and bind it into request context so use cases can call ctx.gate.authorize(...). For tenant, ownership, or role-heavy rules, add a matrix test with createPolicyTester(...) from @beignet/core/testing.

Generate a port adapter

Use make adapter after generating a port when you are ready to replace the generated throwing stub with a concrete infra implementation.

beignet make port email
beignet make adapter email

The command writes infra/email/email-adapter.ts and replaces the generated inline infra stub with email: createEmailAdapter(). The adapter still throws by default; replace its implementation with real infrastructure code while keeping use cases behind the port interface. If the infra wiring was already customized, the command stops instead of guessing.

Generate a resource

Use make resource when the feature you are building is CRUD-shaped and the main concept is an entity with repository-backed persistence:

beignet make resource projects
beignet make resource projects --authorization --tenant-scoped --events --soft-delete

make resource generates a CRUD-shaped slice with list, create, get, update, and delete contracts, use cases, route handlers, repository methods, a policy starter, tests, and feature-specific not-found and conflict catalog errors. Use make feature for workflows and capabilities that do not map cleanly to a REST resource.

Generated list routes use cursor pagination with limit, cursor, name, sortBy, and sortDirection query parameters. Their contract pairs the query schema with an explicit transport so the client, server, and OpenAPI document use the same URL representation. The generated use case normalizes cursor pages, validates opaque base64url cursors against the selected sort, and passes one repository query object into memory and Drizzle adapters. Adapters filter name with case-insensitive contains matching, sort only by createdAt or name plus id, and fetch limit + 1 records to derive nextCursor without a count query.

Generated resources also include optimistic concurrency by default. Responses include a numeric version; update request bodies must send that version back; memory and Drizzle repositories include the version in the update check and increment it on success. Stale updates become the generated <Resource>Conflict catalog error.

Use --authorization to generate authorization metadata, policy wiring, and use-case ctx.gate.authorize(...) calls. Use --tenant-scoped to scope repository reads and writes with a branded TenantScope; generated use cases call requireTenantScope(ctx), repository ports require scope: TenantScope, and adapters unwrap the storage key with tenantScopeId(scope). doctor --strict also warns when tenant-scoped generated repositories lose that boundary, when hand-authored Drizzle ports expose raw tenantId or workspaceId app-facing methods, or when scoped adapters stop using tenantScopeId(scope) predicates. Use --events to generate created, updated, and deleted domain events and publish them through ctx.ports.eventBus. The starter ships no event bus, so --events also wires one: it adds eventBus: EventBusPort to AppPorts, defers the key in infra/port-wiring.ts, registers createMemoryEventBusProvider() in server/providers.ts, and adds the @beignet/provider-event-bus-memory dependency. The wiring is skipped when the ports file already mentions eventBus, so apps that wired their own bus are left untouched. Use --soft-delete when delete routes should archive rows with deletedAt while list, get, and update operations continue to read only active records.

In standard apps, the command also creates infra/db/schema/projects.ts, infra/projects/drizzle-project-repository.ts, and registers the repository in infra/db/repositories.ts.

Generate tasks, events, jobs, notifications, listeners, schedules, and uploads

Use feature artifact generators when a workflow grows beyond a single use case:

beignet make task posts.backfill-search
beignet make event posts.published
beignet make job posts.send-published-email
beignet make broadcast posts.changes
beignet make notification posts.published
beignet make listener posts.enqueue-published-email --event posts.published
beignet make schedule posts.daily-summary --cron "0 9 * * *" --timezone America/Chicago --route
beignet make upload posts.attachment
beignet make upload posts.attachment --ui

These commands use feature.name format and write colocated feature files:

  • features/posts/tasks/backfill-search.ts
  • features/posts/domain/events/published.ts
  • features/posts/jobs/send-published-email.ts
  • features/posts/notifications/published.ts
  • features/posts/listeners/enqueue-published-email.ts
  • features/posts/schedules/daily-summary.ts
  • features/posts/uploads/attachment.ts

Uploads always add a feature-root client-safe manifest so server constraints and browser input hints share one source of truth. In full-stack apps, --ui also writes features/posts/client/attachment-upload.ts, features/posts/components/attachment-uploader.tsx, and features/posts/tests/attachment-uploader.test.tsx, then installs @beignet/react-uploads. The component handles progress, cancellation, failures, reset, and completion. API-only apps reject --ui before writing; omit it to generate the backend upload workflow alone.

Upload generation preflights central registration before writing any feature or provider files. If an app has replaced the generated uploadRegistry = defineUploads({ ... }) shape, the command fails with the exact registry entry to add manually; it never reports success with an unregistered upload.

Each command creates or updates the folder's index.ts with an exported registry such as postTasks, postJobs, postNotifications, postListeners, postSchedules, or postUploads. Task generators also create or update server/tasks.ts, which is the registry and lifecycle boundary used by beignet task run <name> --tenant acme --input '{"dryRun":true}'. New task registries include createTaskContext(...) and stopTaskContext(...) placeholders so provider startup and teardown stay out of task definitions. Both receive TaskRunContextArgs from @beignet/core/tasks — the task, task name, parsed input, and the optional --tenant value — so tenant scoping stays out of task input schemas.

make outbox creates server/outbox.ts and a bounded app/api/cron/outbox/drain route. On Next.js 15.1 or newer it also wraps the database Unit of Work from a server-local provider with createObservedUnitOfWork(...) and schedules one push-assisted drain through after(). Older Next.js versions retain valid cron-only wiring. make event and make job create that outbox path on first use and register the feature's postEvents and postJobs registries in defineOutboxRegistry({...}); beignet doctor reports events and jobs the registry cannot deliver. Keep the route as the durable recovery sweep even when the deferred trigger is present.

make job also ensures jobs: JobDispatcherPort is declared and bound. When the app has no job dispatcher yet, it defers the port and installs an app-owned inline dispatcher provider; existing direct wiring and providers such as Inngest are preserved. Registry membership alone is not execution wiring. Adding the jobs-inngest or jobs-bullmq preset later replaces only that marked generated fallback; an unmarked app-owned inline provider is reported as a conflict instead of being removed. beignet doctor warns when an outbox registry contains jobs but the configured ports and port-wiring files do not declare and bind or defer jobs. Keep that key and the registry's jobs entries explicit; doctor reports indeterminate registry or port-wiring shapes when they prevent it from verifying required wiring, and the generator stops rather than replacing custom spread- or helper-based wiring it cannot verify.

Generators wire the provider-backed ports their output depends on. make event (and make resource --events) wires eventBus: EventBusPort with createMemoryEventBusProvider() from @beignet/provider-event-bus-memory, including the package dependency. Listener generation also installs provider lifecycle wiring that registers the central listener registry in start(), waits for its initial readiness with the default 10-second registry deadline, and awaits cleanup in stop(). make notification wires mailer: MailerPort with createMemoryMailerProvider() and notifications: NotificationPort with createInlineNotificationsProvider(), both dev-default providers from @beignet/core. Each port is wired independently — added to AppPorts, deferred in infra/port-wiring.ts, and registered in server/providers.ts — and skipped when the ports file already has the key, so providers such as resend (which contributes mailer) and app-owned adapters are left untouched. Swap the dev-default provider for a production adapter without rerunning the generator; the port stays the same.

Task, job, listener, schedule, and notification generators define artifacts through app-owned context-bound builders in lib/. Each generator creates the builder file when it is missing:

  • lib/routes.ts exports defineRoute and defineRouteGroup from createRoutes<AppContext>() (@beignet/core/server) and ships with every app
  • lib/tasks.ts exports defineTask from createTasks<AppContext>() (@beignet/core/tasks)
  • lib/jobs.ts exports defineJob from createJobs<AppContext>() (@beignet/core/jobs)
  • lib/listeners.ts exports defineListener from createListeners<AppContext>() (@beignet/core/events)
  • lib/schedules.ts exports defineSchedule from createSchedules<AppContext>() (@beignet/core/schedules)
  • lib/notifications.ts exports defineNotification from createNotifications<AppContext>() (@beignet/core/notifications)

Event generators use @beignet/core/events directly and upload generators use @beignet/core/uploads. --route also writes a Next.js cron route under app/api/cron/<feature>/<name>/route.ts. Generated cron routes require CRON_SECRET and record schedule start, completion, and failure events in devtools. Generated uploads include starter metadata, constraints, authorization, storage key, storage metadata, and completion hooks. Use make upload <feature.name> --ui in a full-stack app to add the connected React client and uploader.

Use beignet outbox drain when you need to drain durable events and jobs from an app-owned CLI, CI, or worker entrypoint:

beignet outbox drain --batch-size 100 --concurrency 4

The runner loads server/outbox.ts or paths.outbox. That module should export outboxRegistry, createOutboxDrainContext(...), and optionally stopOutboxDrainContext(...). The context owns app ports and provider lifecycle, so the CLI drain uses the same outbox, event bus, jobs, instrumentation, actor, and tenant decisions as the scheduled drain route. The drain validates all transports required by the registry before claiming a batch, so missing eventBus or jobs wiring does not consume attempts. Delivery is serial by default. --concurrency enables unordered parallel delivery for handlers that can safely overlap.

The drain report includes abandonedDeadLettered, settlementFailed, and leaseLost. When either uncertainty counter is nonzero, the CLI prints the complete report and exits 1; the equivalent MCP operation returns the same JSON as an error-bearing tool result. Investigate storage availability and worker clock synchronization before retrying.

Inspect and recover failed deliveries through the same app-owned operational context:

beignet outbox list --status deadLettered
beignet outbox show <id>
beignet outbox requeue <id> --reset-attempts
beignet outbox purge --before 2026-06-01T00:00:00Z --dry-run
beignet outbox prune --before 2026-06-01T00:00:00Z --dry-run

purge only targets dead-lettered messages; prune only targets delivered messages. Both support bounded --limit runs and --dry-run previews.

There is intentionally no separate beignet jobs drain command. Outbox-backed jobs drain through the outbox beside durable events. Direct provider jobs should use provider-owned worker entrypoints, such as a BullMQ worker built with createBullMQJobWorker(...) or the generated Inngest server/inngest.ts registry. Next apps receive app/api/inngest/route.ts; non-Next apps receive a createAppInngestFunctions(...) factory for their runtime adapter. When the Inngest provider is installed, beignet make job maintains the central registry.

Use beignet schedule run when you need to run a schedule explicitly from a local shell, CI job, or worker entrypoint:

beignet schedule run posts.log-daily-summary --scheduled-at 2026-01-01T09:00:00.000Z

The runner loads server/schedules.ts or paths.schedules. That module should export a schedules array, createScheduleContext(...), and optionally stopScheduleContext(...). Omit --payload when the schedule should build its own payload from createPayload(...); pass --payload when a provider or manual run supplies the payload.

Operational CLI commands are bounded entrypoints. Use them from a local shell, CI job, release job, scheduler, or worker host. Do not start outbox drains, queue consumers, or interval polling from provider lifecycle hooks in serverless apps.

The matching library functions and report types are exported from @beignet/cli for orchestrators that do not use the bin:

import {
  runAppTask,
  runOutboxList,
} from "@beignet/cli";

const task = await runAppTask({
  name: "reports.backfill",
  input: { dryRun: true },
});
const failed = await runOutboxList({ status: "deadLettered", limit: 25 });

runAppSchedule, runOutboxDrain, runOutboxShow, runOutboxRequeue, runOutboxPurge, and runOutboxPrune are exported from the same entrypoint. 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.

make listener expects the event file to exist at the canonical generated path. Run make event <feature.event> first, then generate listeners for that event.

make feature and make resource are idempotent for unchanged generated files: repeated runs skip identical files and avoid duplicate wiring. If a generated file exists with different content, the command stops unless you pass --force.

Generate an encryption key

beignet encryption key
beignet encryption key --json

Prints a new base64:-prefixed 256-bit key for createEncryption(...) from @beignet/core/encryption. JSON output contains schemaVersion: 1 and key. The command works outside an app and does not read or update environment files. Put the key into your server secret store; keep command output out of shared logs and never replace a key without retaining the keys needed to read existing data. See Encryption for wiring, rolling rotation, and backup recovery.

Database lifecycle

Use beignet db from an app root to run database workflow scripts through one stable framework command surface:

beignet db generate
beignet db migrate
beignet db status
beignet db seed
beignet db reset

The CLI delegates to app-owned package scripts named db:generate, db:migrate, db:status, db:seed, and db:reset. In standard Drizzle apps, those scripts run Drizzle Kit and the generated status, seed, and reset entrypoints. Status is read-only: exit 0 means current, exit 2 means pending, and any other non-zero exit means inspection failed. This keeps database behavior app-owned while making the lifecycle discoverable through Beignet.

When an app adopts Beignet's Drizzle-backed operational ports for audit, idempotency, or outbox, run beignet db schema sync first. It idempotently brings the app-owned schema file that re-exports Beignet provider tables (infra/db/schema/beignet.ts by default) in sync with the installed providers and updates the schema index. Run beignet db generate afterward so the app's Drizzle Kit script creates the matching migration.

beignet db checks standard prerequisites before running those scripts. If the script is missing, Drizzle Kit has no drizzle.config.*, or the standard status/seed/reset entrypoint was removed, the error points to the exact file or package script to restore.

beignet db <command> --json retains the final 64 KiB from each output stream and sets outputTruncated: true when earlier output was discarded. Human terminal output remains streamed directly by the app-owned script.

db reset is intentionally app-owned because destructive behavior depends on the environment. The standard starter refuses to reset non-local database URLs unless BEIGNET_ALLOW_DATABASE_RESET=true is set. Drizzle starters also include infra/db/test-database.ts, which creates an isolated test database for repository and persistence tests: an in-memory libSQL database for SQLite, an in-process PGlite database for Postgres, or a real server via MYSQL_TEST_URL for MySQL.

Coding agents can run the same lifecycle without leaving MCP. Call db with { command: "generate" | "migrate" | "seed" | "reset", dryRun?, timeoutMs? } for the exact versioned CLI/library report. Output is tail-bounded, requests cancel the active process tree, and commands default to a ten-minute timeout. Call db_schema_sync with { dialect?, tables?, output?, dryRun? } for the same report as beignet db schema sync --json. For lifecycle commands, dryRun validates prerequisites and reports the app-owned script without executing it; it does not simulate the script's SQL or data changes. Call the separate read-only db_status tool with { timeoutMs? } for the same report as beignet db status --json.

Generate feature-owned test factories and local/demo seeds with:

beignet make factory posts.post
beignet make seed posts.demo-posts

Factories are written under features/posts/tests/factories/ and persist through app-owned repository ports. Seeds are written under features/posts/seeds/; make seed creates the app-owned server/seed.ts entrypoint and db:seed script when they are missing.

Generated feature and use-case tests use Beignet's test context helpers so request IDs, actors, tenants, and ports follow the same pattern as the testing docs. Generated route tests boot createTestApp(...) with the app's server/context.ts blueprint, so HTTP coverage exercises the same identity resolution as production.

Preview writes without changing files:

beignet make feature projects --dry-run

Use JSON output when another tool needs the exact planned changes:

beignet make feature projects --dry-run --json

Inspect app routes

Inside an app, list the contracts the CLI can match to Next.js route handlers:

beignet routes

Use JSON output when you want to feed the route catalog into another tool:

beignet routes --json

Map app architecture

Build a deterministic, source-backed graph of the app:

beignet map
beignet map --feature issues
beignet map --json --kind route,use-case,event,listener
beignet map --changed
beignet map --changed --base origin/main --json

The human view summarizes contracts, use cases, workflows, ports, UI, and tests per feature. JSON is a versioned schemaVersion: 1 payload with features, contracts, route groups, use cases, policies and abilities, workflow definitions and registries, agent capabilities, ports, app/package providers, tables, OpenAPI exposure, tests, cross-feature dependencies, doctor/lint findings, and unresolved references. Every node has a stable ID and source; every edge carries source evidence and confidence.

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, while imports, re-exports, literal dynamic imports, and CommonJS require() calls contribute dependency evidence across mixed-language graphs.

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. When one of those tests imports changed source, map --changed can report it as an exact reverse-import consumer. That is impact evidence, not proof that the test covers every behavior affected by the change.

The JSON app map retains failed local module references as explicit local_import_unresolved entries instead of silently omitting their edges. It also reports contract_declaration_unresolved when an exported direct or contract-group declaration has a method, path, or group prefix that cannot be derived statically. Registration diagnostics identify their declaration through a structured subject.file and subject.exportName; registration status in the map uses that identity rather than matching human-readable messages.

--feature keeps that feature plus its direct relationships, while --kind accepts one or a comma-separated list of node kinds. These projections make the same full graph practical for coding-agent context windows. The command is report-only and does not fail because doctor or lint findings are present.

--changed maps the current Git change set to potentially affected concepts. Without --base, it preserves staged index changes, unstaged worktree changes, and untracked files instead of collapsing them into one net diff against HEAD. With --base <ref>, it also includes committed branch changes from the merge base of that already-local ref through HEAD; Beignet never fetches the ref. The bounded report distinguishes 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 are marked app-wide. 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. Deleted declarations, unresolved imports, changed files with no mapped concept, and other static-analysis blind spots remain visible as 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 explains potential impact; it does not run checks or claim that a change is correct. Use beignet check after inspecting the report. --changed cannot be combined with --feature or --kind, and --base requires --changed.

The same report is available to library callers:

import { mapAppChanges } from "@beignet/cli";

const impact = await mapAppChanges({ cwd: "apps/api", base: "origin/main" });

Explain mapped app concepts

Resolve one app-map target into deterministic, source-backed context:

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_UNREGISTERED

<kind> accepts every app-map node kind plus diagnostic: features, contracts, routes and route groups, use cases, policies and abilities, workflow artifacts, agent capabilities, registries, ports and providers, tables, OpenAPI documents, entrypoints, and tests. Targets accept stable IDs, runtime or declaration names, source selectors such as file#export, and applicable aliases such as an HTTP method/path or provider port.

Human output summarizes relevant relationships, conventions, findings, suggested files, and follow-up commands. --json returns the versioned schemaVersion: 1 payload used by the MCP explain tool. Feature explanations bound their relationship list and report full and returned counts; use beignet map --feature <name> --json or explain a directly related concept for the omitted detail. Provider explanations include the matching entry in the configured provider registry when static provider audit proves it. Human output names the app root, and command actions in JSON carry their own cwd, so an explanation requested with --cwd stays copy-pasteable. Explain is report-only and never changes the app.

Run the validation loop

Run check when you want the whole validation loop as one command:

beignet check

It runs 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; the command exits non-zero when any step fails. Missing package scripts are reported as skipped, never as failures. Failed package-script output retains at most the last 64 KiB of each stream. 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.

When the app is inside a Git worktree, check captures the repository HEAD, index, worktree, and non-ignored untracked files before and after validation. If the ending source-state hash differs from the baseline, the result is stale, ok is false, and the command exits non-zero even when every validation step passed. Rerun check against the resulting source. Untracked build and coverage output excluded by Git ignore rules does not affect the hash. Outside Git, validation continues and reports source provenance as unavailable; corrupt or unreadable Git state still fails loudly.

Use beignet check --fix to apply doctor's eligible low-risk fixes before the provenance baseline and validation steps. beignet check --json returns a versioned payload (schemaVersion: 1) with the step list, statuses, captured failure output, applied fixes, and starting and ending provenance. Add --preflight to run the disconnected runtime environment gate after strict doctor. Use --preflight-connect when the same check should also inspect migration status and dependency health; it implies --preflight. The individual commands below still work when you need one check alone or its --format github output.

Gate the deploy environment

beignet preflight
beignet preflight --connect
beignet check --preflight
beignet check --preflight-connect

preflight is the runtime production gate, distinct from the static doctor checks: it reads the environment the process actually runs with. It verifies every individually required provider env var and accepts mutually exclusive credentials only when one complete requiredEnvAlternatives configuration is 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, and warns when logging or error reporting is absent or inert. --connect first runs the app-owned db:status script when available, failing before server boot on pending migrations or inspection failure, then runs every port's checkHealth(). Older apps without the script receive a compatibility note. The flag is opt-in because it needs network access and real credentials. Use --connect-timeout-ms <ms> to change the default 5000 ms limit for migration status and each dependency health check. Timeout findings include the configured duration, and bounded status output says when only the retained tail is shown. --env-file merges a dotenv-style file under the environment for local rehearsal, --env-module and --server-module override the default lib/env.ts and server/index.ts paths, and --json emits machine-readable output with schemaVersion: 1. Run it in the deploy pipeline where production configuration is present; it exits 1 on any error finding.

beignet task run, beignet schedule run, and beignet outbox drain use an optional ctx.ports.errorReporter from their app-owned context. They report terminal command failures, dead letters, lease failures, and settlement failures, then perform a bounded flush before stopping context. Retryable outbox deliveries remain instrumentation/log events rather than incident reports.

Lint app architecture

Run lint when you want the CLI to enforce Beignet dependency direction:

beignet lint

The command analyzes .ts, .tsx, .mts, .cts, .js, .jsx, .mjs, and .cjs files. It recognizes imports and re-exports, require(), import assignments, and literal dynamic imports. It fails when app layers reach into runtime or framework layers, including domain, use case, workflow, policy, port, contract, route, schema, seed, agent capability, feature helper, infra, and component files importing infra/, UI components, route handlers, server modules, client modules, provider packages, Next.js, React, or database vendors in the wrong direction. Computed require() and import() expressions inside constrained layers fail because lint cannot verify their destination; use a string-literal specifier or move runtime loading behind an allowed boundary. Client-safe graphs reject Node built-ins whether they use node:fs or a bare specifier such as fs/promises.

Local value-import checks follow feature-root and other local helpers. For example, `use-case