Beignet API reference
    Preparing search index...

    Module @beignet/cli

    @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.

    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
    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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(...).

    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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
    

    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
    

    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" });

    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 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.

    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.

    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 -> history.ts -> infra reports the complete chain instead of treating history.ts as an unknown layer. Every module inside the configured feature root is classified; allowed feature-root helpers remain helpers, while near-miss canonical names such as usecases/ report the expected use-cases/ path. Feature-specific domain files still cannot import another feature's domain unless the target is features/shared/domain. Workflow folders include feature-owned jobs, listeners, notifications, schedules, tasks, and uploads. Agent capabilities may adapt use cases but core application layers cannot depend back on those adapters. Seeds may compose factories and app ports, but not concrete infra, providers, UI, or runtime composition.

    Local imports are resolved with the app's complete tsconfig.json, or its jsconfig.json when no TypeScript config exists, before dependency direction is classified. That includes baseUrl, every paths alias such as @/*, ~/*, or #app/*, and aliases with multiple targets. The CLI uses TypeScript's config parser and resolver, so JSON comments and extended configs follow the same rules as the compiler. Beignet enables JavaScript resolution for this read-only analysis without changing the app's compiler configuration.

    Import diagnostics include 1-based line and column positions and preserve separate occurrences of the same module specifier. Human output prints clickable file.ts:12:3 locations; transitive and runtime-boundary diagnostics report the chain root file and the full import chain in the message instead.

    Use JSON output in CI or custom scripts, or GitHub annotations in workflows:

    beignet lint --json
    beignet lint --format github

    Run doctor when you want a framework integrity report:

    beignet doctor
    

    Today it detects:

    • Contracts without a matching Next.js route handler
    • Contract query schemas without an explicit second .query(...) transport
    • Feature route groups that are not registered in the central route list
    • Feature schedules and tasks that are not registered in server/schedules.ts or server/tasks.ts
    • Feature events with listeners, and feature jobs, that are missing from defineOutboxRegistry({...}) when the app uses outbox delivery
    • Outbox registries with jobs but no declared and bound or deferred jobs application port
    • Feature listeners missing from server/listeners.ts or otherwise not referenced by registerListeners(...)
    • Listener wiring that does not register in start(), await initial readiness, and await asynchronous cleanup in stop()
    • Opted-in runtime manifests that omit generated workflow registries
    • Next.js route handlers that do not map to known contracts
    • OpenAPI drift in direct arrays, exported contract lists, and contractsFromRoutes(routes) route registries
    • OpenAPI entries for contracts outside the registered route surface
    • Strict-mode canonical app conformance drift, including missing app context, server route/provider registries, app ports, client helpers, typed route groups, and local AppContext redeclarations
    • Partially wired generated feature slices
    • CRUD resource slices missing generated contracts, use cases, repository methods, route entries, not-found catalog errors, or strict-mode test coverage
    • Route-owned catalog errors declared on contracts but missing from features/shared/errors.ts
    • Feature runtime appError(...) calls that are not declared on the feature's contracts
    • Feature layers whose normalized name or known alias points at a canonical location, such as usecases/ instead of use-cases/ or events/ instead of domain/events/; doctor and lint report the same expected path
    • Authorization metadata whose ability is missing from feature policy coverage
    • Audit-required metadata without feature audit writes or test assertions
    • Mixed @beignet/* version ranges in package.json, and mixed installed @beignet/* versions in node_modules; the packages release together, so one app should use one version
    • CLI versions that differ from the app's installed @beignet/core, reported as an informational hint that suggests the app-local bun beignet
    • Malformed installed provider package metadata
    • Provider packages without matching canonical app ports
    • Installed lifecycle provider packages that are not registered in server/providers.ts, reported as an informational hint when the provider declares registration as optional, such as @beignet/devtools
    • App database providers registered before the provider that installs db
    • Generated ports without test fakes
    • Drizzle repository adapters that are not wired through infra/db/repositories.ts
    • Repository ports whose infra folder has no repository adapter
    • Missing Drizzle config, schema index exports, database scripts, or standard status/seed/reset entrypoints
    • Drizzle-backed idempotency, outbox, or audit ports without matching table setup in schema files, configured schema sources, migrations, provider setup statements, or app database setup files
    • Single-file infra/db/schema.ts schemas that should move to the infra/db/schema/ directory form
    • Feature tests placed directly under features/<feature>/ instead of features/<feature>/tests/
    • Standard database reset entrypoints that no longer guard non-local resets
    • Feature seeds without a matching feature factory
    • Feature seeds without a db:seed package script
    • Upload definitions without an upload route
    • Upload definitions without explicit size limits
    • Billing slices without a payment webhook route, preferring createPaymentWebhookRoute(...) for payment-port billing
    • Billing checkout contracts missing idempotency metadata or an idempotency-key header
    • Billing webhook use cases that reference ctx.ports.idempotency when AppPorts does not declare an idempotency port
    • Entitlement checks without an AppPorts entitlements declaration
    • Billing entitlement modules that are not wired into infra or server providers
    • Stripe payments configuration paired with local memory payments provider wiring
    • Devtools routes without authorization or an explicit local-only development opt-in
    • Cron routes that do not use CRON_SECRET
    • Installed provider packages without expected environment configuration
    • Better Auth providers without an auth route or trusted-origin configuration, and Better Auth 1.7 Drizzle apps missing account.issuer or the required compound identity index
    • Central servers that omit createSecurityHeadersHooks(...)
    • Credentialed wildcard CORS configuration
    • Notification dispatchers that bypass ctx.ports.notifications
    • Feature artifacts in non-canonical folders

    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.

    Provider package diagnostics come from package-owned beignet.provider metadata in installed package manifests. The CLI does not import provider implementation modules to discover those facts, and it reports malformed metadata before using provider-derived doctor rules.

    Use beignet provider add <preset> to wire a stable provider recipe after app creation. It updates package.json, server/providers.ts, app port types, deferred port wiring, .env.example, and docs/providers.md. Supported presets are flags-openfeature, jobs-bullmq, jobs-inngest, mail-resend, mail-smtp, payments-stripe, search-meilisearch, error-reporting-sentry, rate-limit-upstash, cache-redis, event-bus-redis, locks-redis, storage-s3, and storage-vercel-blob; pass --dry-run --json to preview the exact file changes. The same presets can be selected at create time with --providers.

    The storage presets declare StoragePort from @beignet/core/ports and reuse that declaration if your app already has local storage. Before switching storage providers, remove the previous provider entry, import, and unused provider package; keep the app's storage port and deferred binding.

    Use beignet provider audit when you want a report-only inventory of the provider packages installed in an app. The human audit prints provider metadata validity, registration status, required env, required tables, and declared app ports. beignet provider audit --json returns a versioned schemaVersion: 1 payload with active variants, watchers, and source locations for matching provider-registry entries. Missing alternative credential paths remain grouped in human diagnostics, while JSON preserves each active variant's accepted configurations and status. The command does not fail CI for missing setup; stable, actionable findings belong in doctor.

    Use beignet doctor --strict for CI-oriented checks that may be noisy locally, such as missing generated feature tests, canonical app conformance drift, and feature-owned route errors that appear unused. Strict mode fails on warnings as well as errors; informational hints never affect the exit code, so an installed-but-unregistered optional provider such as @beignet/devtools still passes doctor --strict. Use beignet doctor --json in CI or custom scripts. Doctor JSON includes schemaVersion: 1 plus targetDir, config, strict, convention, contracts, routes, diagnostics, and fixes.

    Use beignet doctor --fix for low-risk maintenance fixes. Today it can add a missing test script, register existing feature route groups in the central route list, register unregistered feature schedule, task, and outbox event/job registries in their existing central registry files, wire fully unregistered feature listener registries into server/listeners.ts and the central listeners provider, register feature jobs in an existing server/inngest.ts, align an opted-in runtime manifest with fully unregistered workflow registries, sync missing default audit, idempotency, and outbox table exports through infra/db/schema/beignet.ts, and repair direct createOpenAPIHandler([...]) arrays when the missing contracts are already imported in the OpenAPI route.

    Registry and runtime-manifest fixes are append-only. When a central file is missing, an anchor is customized beyond recognition, an import is ambiguous, or some artifacts are already registered individually, the fix bails out and the diagnostic stays. The listener fix additionally bails when the app has no eventBus port or the providers array is customized, because apps own their event-bus wiring. Database repair requires an existing schema index, default Beignet table names, a missing beignet.ts or one that still matches beignet db schema sync output, and a schema index where the generated export * can be safely added or retained. It does not generate or apply a migration. Run beignet db generate, beignet db migrate, and beignet db status after accepting that source change. Compatible workflow repairs that share files are combined into the selectable workflows.register-missing operation so apply-all remains atomic.

    Preview the exact changes before applying them with:

    beignet doctor --fix --dry-run
    

    The plan contains stable repair operation IDs, SHA-256 before/after hashes, unified patches, a planId, and the current doctor diagnostics. Human and GitHub output render those diagnostics so every non-zero exit is explained. Apply the complete guarded plan with beignet doctor --fix --plan <planId>, or select operations with --only routes.register-missing,outbox.register-missing. Guarded application fails before writing when any fixable file or available repair changed after planning. Plain doctor --fix and check --fix retain their apply-all behavior for scripts that already use them.

    Run an MCP (Model Context Protocol) server over stdio so coding agents can read app-local guidance and focused feature maps as resources, then call the CLI as tools:

    beignet mcp
    

    The server publishes read-only app guidance and per-feature app maps alongside its tools.

    Resources:

    • beignet://app/guidance - bounded app-local AGENTS.md guidance, with a concise fallback when the file is absent
    • beignet://app/features/{feature} - focused source-backed feature app maps, with available features discoverable through MCP resource listing and feature-name completion

    Tools:

    • app_map - project the source-backed app graph by optional feature, kinds, and includeDiagnostics inputs, or pass { changed: true, base?: "origin/main" } for the same bounded report as beignet map --changed --json (read-only). Changed mode rejects projection and diagnostic inputs
    • explain - explain any source-backed mapped concept or diagnostic with { kind, target }, returning relationships, conventions, findings, suggested files, and follow-up commands (read-only)
    • check - run the complete validation loop and return versioned step results, bounded failure output, durations, starting and ending source provenance, and overall ok. An ending fingerprint that differs from the baseline makes the result stale and unsuccessful. The tool accepts { preflight?: boolean, preflightConnect?: boolean, connectTimeoutMs?: number, timeoutMs?: number }, cancels the active script with the MCP request, and does not apply Beignet fixes. preflightConnect implies preflight and adds migration and dependency checks. App scripts retain their normal side effects, so this is an execution tool rather than a read-only tool
    • db - run generate, migrate, seed, or reset with { command, dryRun?, timeoutMs? }; returns the matching versioned CLI report with bounded output, request cancellation, and a command timeout
    • db_status - inspect the app-owned migration history without mutating the database; returns current, pending, or failed
    • db_schema_sync - sync app-owned Beignet provider-table schema re-exports with { dialect?, tables?, output?, dryRun? }; idempotent but writes source unless dryRun is true
    • task_run - run a registered task with { name, input?, tenant?, module?, timeoutMs? } in an isolated process
    • schedule_run - run a registered schedule with { name, payload?, runId?, attempt?, scheduledAt?, triggeredAt?, source?, module?, timeoutMs? } in an isolated process
    • outbox_inspect - read outbox state with operation-specific list or show inputs (read-only)
    • outbox_run - run the state-changing drain, requeue, purge, or prune operations; purge and prune accept dryRun
    • routes - list app routes and contracts (read-only)
    • doctor - drift diagnostics as JSON, { strict?: boolean } defaults to true (read-only)
    • doctor_fix_plan - return stable repair operation IDs, exact patches, file hashes, and a plan ID without changing files
    • doctor_fix - apply doctor's low-risk fixes; accepts { planId?, fixIds?, strict? }. Omitting planId preserves apply-all; fixIds requires the plan ID returned by doctor_fix_plan
    • lint - architecture dependency-direction diagnostics (read-only)
    • make - run a generator with { artifact, name, ...options }, the same artifact kinds as beignet make
    • provider_add - add a provider setup preset with { preset, dryRun? }

    The db, db_status, db_schema_sync, task_run, schedule_run, outbox_inspect, outbox_run, doctor_fix_plan, and doctor_fix tool outputs match the corresponding CLI/library JSON payloads. Operational app code runs outside the long-lived MCP server with bounded structured results, request cancellation, and process-tree timeouts. Operational module overrides must be app-relative and cannot leave the app root where the 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. Generated apps ship a .mcp.json that runs ./node_modules/.bin/beignet mcp, so Claude Code picks up the app-local CLI version with no configuration; Cursor and VS Code users add the same command to .cursor/mcp.json or .vscode/mcp.json. For one-off usage outside an installed app, run the scoped package, such as bunx @beignet/cli mcp or npx @beignet/cli mcp. Generated apps also include AGENTS.md and CLAUDE.md covering registration, generator, placement, and validation conventions.

    beignet make broadcast issues.changes adds a browser-safe features/issues/channels.ts, a fail-closed authorization use case, features/issues/broadcasts.ts, lib/broadcasting.ts, server/broadcasts.ts, and a thin SSE adapter. It wires the memory broadcast port if missing and reuses an existing conventional auth.required() hook when available. Implement current identity, membership, and resource policy checks before enabling access; then map events to query invalidation and reconcile after reconnect. Run bun install after generation or a provider change to install newly added dependencies.

    Select beignet provider add broadcast-redis before the first generator when publishers and subscribers run in different processes. Set REDIS_BROADCAST_URL and an app/environment-specific REDIS_BROADCAST_PREFIX. The memory and Redis presets are alternatives; replace the existing provider when switching.

    beignet make inbox --broadcast composes a recipient-authorized channel, list/count query mapping, and a publication job recorded in the inbox write's transaction. make inbox selects this recipe automatically if a broadcast port already exists. Rerun it after adding broadcasts to a standard generated inbox; customized writes require explicit transaction edits. Configure the outbox drain and mount the generated client helper with the signed-in user ID.

    Configured paths are broadcasts (default server/broadcasts.ts), broadcastingBuilder (lib/broadcasting.ts), and broadcastRoute (app/api/broadcasts/route.ts for Next, server/broadcast-route.ts for web). Web hosts pass their assembled server to the generated createAppBroadcastRoute(server) factory and mount its returned GET handler. Generated adapters use the default maxLifetimeMs: 60_000. Set a positive safe integer up to 3_600_000 (one hour), such as 240_000 for four-minute streams, when the host permits it. Leave setup/cleanup headroom below its request deadline; a Next route can use maxDuration = 300 for a four-minute stream. Longer streams reduce renewal/refetch frequency but increase the interval between authorization checks. The browser honors the advertised lifetime and reconciles after every renewal. Doctor accepts these bounded lifetimes.

    Channel contracts are schema modules; server bindings and app-owned channel.ts notification handlers are workflow modules. Browser imports of server bindings are rejected.

    routes shows streaming endpoints separately from HTTP contracts. map and explain recognize channel declarations and bindings. doctor follows bindings through registry array literals, named arrays, and spreads, including readonly arrays. It checks evident registration and lifetime drift; it cannot prove access policies, transaction ordering, or host suitability. MCP's make tool accepts artifact: "broadcast" and broadcast: true for artifact: "inbox". See the broadcasting guide.

    MIT

    config