--- url: https://toolchain.deltabox.studio/config.md --- # Config types The toolchain has three configuration surfaces, all derived from the same Zod model: * **Programmatic** — `defineConfig({ toolchain: { ... } })` in `dbx.config.ts`; typed as `ToolchainConfig` (see the [TypeScript guide](/guide/typescript)). * **CLI** — the `dbx` command options, generated from the command descriptors; see the [CLI reference](/config/cli). * **Environment** — schema leaves can opt into env vars with `.meta({ env })`; see the [environment reference](/config/env) and the [Config Generator](/guide/config-generator/). The exhaustive object reference is [dbx.config](/config/reference); the editor JSON Schema is [config.schema.json](https://toolchain.deltabox.studio/config.schema.json). ## Fields Every generated page uses the same `ConfigMeta` fields: `description`, `required`, `deprecated`, `example`, `secret`, `scope`, `public`, `defaultValue`, the env fields `envName`, `envPath` and more. --- --- url: https://toolchain.deltabox.studio/config/cli.md --- # CLI reference Generated from the same `ToolchainCommand` descriptors the `dbx` binary registers. Run `yarn config:docs` after changing a command. | Command | Description | | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | [`dbx projects`](#projects) | Discover projects and write .dbx/toolchain/projects.json. | | [`dbx sync`](#sync) | Regenerate every managed file from dbx.config.*. | | [`dbx watch`](#watch) | Sync once, then watch key files and re-sync on change. | | [`dbx reset`](#reset) | Remove generated files, the .dbx/toolchain manifest and/or the dbx config from a project. | | [`dbx providers`](#providers) | List the tooling providers (Vite+, ...) and their commands. | | [`dbx tsconfig`](#tsconfig) | Generate workspace tsconfig.json files from the default tsconfig (aliases + references). | | [`dbx cleanup`](#cleanup) | Remove build artifacts (*.tsbuildinfo, dist/). | | [`dbx cleanup:turbo`](#cleanup-turbo) | Remove the turbo cache (.turbo/). | | [`dbx workflow`](#workflow) | Trigger generated workflows with their vendor CLI (`gh`, `glab` or `az`). | | [`dbx changelog`](#changelog) | Scan commits, derive the semver bump and update the CHANGELOG.md files + versions. | | [`dbx migrate`](#migrate) | Migrate Biome / ESLint / Prettier / Dprint / Oxc config, or fold vite.config.\* into dbx.config. | | [`dbx migrate:list`](#migrate-list) | List the registered migrations and whether each is detected. | | [`dbx project:init`](#project-init) | Scaffold a single-package TypeScript project. | | [`dbx project:init:monorepo`](#project-init-monorepo) | Scaffold a monorepo root (add --example for one example workspace). | | [`dbx project:init:workspace`](#project-init-workspace) | Scaffold a monorepo workspace package into an existing monorepo root. | | [`dbx project:config`](#project-config) | Generate dbx.config.\* for existing projects that are missing one. | | [`dbx template:list`](#template-list) | List templates (builtin + local + opted-in online sources). | | [`dbx template:init`](#template-init) | Write a template.json to a directory, making it a local template. | | [`dbx template:add`](#template-add) | Record a template source in .dbx/toolchain/templates.json. | | [`dbx template:remove`](#template-remove) | Remove a source from .dbx/toolchain/templates.json. | | [`dbx template:registry`](#template-registry) | Manage the global template search paths (and sync them to the VS Code setting). | | [`dbx registry`](#registry) | List or manage the template/provider registries. | | [`dbx template:info`](#template-info) | Show one template by id. | | [`dbx create`](#create) | Scaffold a project from a template (builtin, local or Vite+). | | [`dbx vite`](#vite) | Run any Vite+ command through `vp` (eg. `dbx vite dev`). | | [`dbx vite:create`](#vite-create) | Vite+: Create a new project from a template. | | [`dbx vite:migrate`](#vite-migrate) | Vite+: Migrate an existing project to Vite+. | | [`dbx vite:config`](#vite-config) | Vite+: Configure hooks and agent integration. | | [`dbx vite:hooks`](#vite-hooks) | Vite+: Manage the Git hook dispatcher. | | [`dbx vite:staged`](#vite-staged) | Vite+: Run linters on staged files. | | [`dbx vite:install`](#vite-install) | Vite+: Install dependencies with the detected package manager. | | [`dbx vite:env`](#vite-env) | Vite+: Manage Node.js and package managers. | | [`dbx vite:dev`](#vite-dev) | Vite+: Run the Vite development server. | | [`dbx vite:check`](#vite-check) | Vite+: Run format, lint and type checks. | | [`dbx vite:lint`](#vite-lint) | Vite+: Lint code with the active linter (Oxlint via Vite+). | | [`dbx vite:fmt`](#vite-fmt) | Vite+: Format code with the active formatter (Oxfmt via Vite+). | | [`dbx vite:test`](#vite-test) | Vite+: Run tests through bundled Vitest. | | [`dbx vite:run`](#vite-run) | Vite+: Run package scripts and monorepo tasks. | | [`dbx vite:exec`](#vite-exec) | Vite+: Execute a binary from local node_modules. | | [`dbx vite:node`](#vite-node) | Vite+: Run a Node.js script in the Vite+ env. | | [`dbx vite:dlx`](#vite-dlx) | Vite+: Run a package binary without installing it. | | [`dbx vite:cache`](#vite-cache) | Vite+: Manage the task cache. | | [`dbx vite:build`](#vite-build) | Vite+: Build for production. | | [`dbx vite:pack`](#vite-pack) | Vite+: Build libraries for npm or standalone binaries. | | [`dbx vite:preview`](#vite-preview) | Vite+: Preview the production build. | | [`dbx vite:add`](#vite-add) | Vite+: Add packages to dependencies. | | [`dbx vite:remove`](#vite-remove) | Vite+: Remove packages from dependencies. | | [`dbx vite:update`](#vite-update) | Vite+: Update packages. | | [`dbx vite:dedupe`](#vite-dedupe) | Vite+: Deduplicate dependencies. | | [`dbx vite:outdated`](#vite-outdated) | Vite+: Check for outdated packages. | | [`dbx vite:list`](#vite-list) | Vite+: List installed packages. | | [`dbx vite:why`](#vite-why) | Vite+: Explain why a package is installed. | | [`dbx vite:info`](#vite-info) | Vite+: View package metadata. | | [`dbx vite:link`](#vite-link) | Vite+: Link local packages. | | [`dbx vite:unlink`](#vite-unlink) | Vite+: Unlink local packages. | | [`dbx vite:rebuild`](#vite-rebuild) | Vite+: Rebuild native modules. | | [`dbx vite:pm`](#vite-pm) | Vite+: Forward a command to the package manager. | | [`dbx vite:toolchain`](#vite-toolchain) | Vite+: Show Vite+ tool versions. | | [`dbx vite:upgrade`](#vite-upgrade) | Vite+: Update `vp` itself. | | [`dbx vite:implode`](#vite-implode) | Vite+: Remove `vp` and all related data. | ## `dbx projects` Discover projects and write .dbx/toolchain/projects.json. * `--cwd ‹dir›` — Root to scan (default: cwd). * `--max-depth ‹n›` — Max directory depth. (default: `5`) * `--no-write` — Do not write the graph file. * `--json` — Print the graph as JSON. ## `dbx sync` Regenerate every managed file from dbx.config.\*. * `--cwd ‹dir›` — Project root (default: cwd). * `-q, --quiet` — Only log errors. * `--check` — Do not write; fail if files are out of date. * `--watch` — Keep running and re-sync on change. * `--recursive` — Sync every project that has its own dbx.config.\* in dependency order. * `--force` — Run even when no dbx.config.\* is present. * `--only ‹tasks›` — Only run the named sync task(s), comma-separated (eg. `tsconfig`). * `--json` — Print the sync result (including removed files) as JSON. ## `dbx watch` Sync once, then watch key files and re-sync on change. * `--cwd ‹dir›` — Project root (default: cwd). * `-q, --quiet` — Only log errors. * `--force` — Run even when no dbx.config.\* is present. * `--list` — Print the resolved input globs (+ tsconfig file names) instead of watching. * `--json` — With --list: print the inputs as JSON. ## `dbx reset` Remove generated files, the .dbx/toolchain manifest and/or the dbx config from a project. * `--cwd ‹dir›` — Project root (default: cwd). * `--recursive` — Reset every discovered project under the root. * `--include ‹pattern...›` — Only projects matching these patterns (glob or substring). * `--exclude ‹pattern...›` — Skip projects matching these patterns (glob or substring). * `--generated` — Remove only the generated/managed files. * `--manifest` — Remove only the .dbx/toolchain directory. * `--config` — Also remove dbx.config.\*. * `--all` — Remove generated files, the manifest and the config. * `--dry-run` — Report what would be removed without deleting. * `--json` — Print the result as JSON. ## `dbx providers` List the tooling providers (Vite+, ...) and their commands. * `--json` — Print the providers (with actions) as JSON. ## `dbx tsconfig` Generate workspace tsconfig.json files from the default tsconfig (aliases + references). * `--cwd ‹dir›` — Repo root to scan (default: cwd). * `--no-aliases` — Skip path aliases. * `--composite` — Emit composite + references. * `--refs` — Wire project references. * `--auto` — Auto-wire the root workspaces tsconfig paths/references from the project graph (default false). * `--no-auto` — Only use the paths/references already in the root workspaces tsconfig. * `--auto-aliases` — Auto-wire path aliases (defaults to --auto). * `--no-auto-aliases` — Do not auto-wire path aliases. * `--auto-refs` — Auto-wire project references (defaults to --auto). * `--no-auto-refs` — Do not auto-wire project references. * `--auto-src-dir ‹dir›` — Source directory for auto entry points (default: the workspace tsconfig rootDir/baseUrl, then src). * `-i, --indent ‹n›` — JSON indentation. * `--glob ‹glob...›` — Glob(s) for the default tsconfig files. * `--exclude ‹glob...›` — Glob(s) to exclude (on top of the common ignored paths). * `--no-gitignore` — Do not respect .gitignore files. * `--default-file ‹file›` — Default tsconfig file name. * `--build-file ‹file›` — Build tsconfig file name. * `--root-file ‹file›` — Workspaces tsconfig file name. * `--tsconfig ‹file›` — Resolve this tsconfig instead of \/tsconfig.json. * `--root ‹dir›` — Root used to detect/resolve the tsconfig (default: cwd). * `--json` — Print the resolved { alias, paths, references } object. * `--alias` — Print only the resolved Vite/Vitest aliases. * `--paths` — Print only the resolved compilerOptions.paths. * `--references` — Print only the resolved project references. * `--no-extends` — Do not follow the tsconfig extends chain. * `--restore [kind]` — Remove the applied tsconfig overrides (all, or base|root|default|build) using the manifest snapshots. ## `dbx cleanup` Remove build artifacts (\*.tsbuildinfo, dist/). * `--cwd ‹dir›` — Directory to clean (default: cwd). * `--pattern ‹glob...›` — Override the match globs. ## `dbx cleanup:turbo` Remove the turbo cache (.turbo/). * `--cwd ‹dir›` — Directory to clean (default: cwd). ## `dbx workflow` Trigger generated workflows with their vendor CLI (`gh`, `glab` or `az`). * `[workflows]` — Workflow ids, custom ids, or file names (default: every enabled workflow). * `--cwd ‹dir›` — Project root (default: cwd). * `--ref ‹ref›` — Git ref to run the workflow on. * `--gh ‹cmd›` — The gh executable (default: gh). * `--glab ‹cmd›` — The glab executable (default: glab). * `--az ‹cmd›` — The az executable (default: az). * `--list` — List the enabled workflows (and where they are written). * `--json` — With --list: print the workflows as JSON. ## `dbx changelog` Scan commits, derive the semver bump and update the CHANGELOG.md files + versions. * `--range ‹range›` — Git revision range to scan. * `--dry-run` — Print the proposed entry without writing. * `--version ‹semver›` — Override the version. * `--base-version ‹semver›` — Bump from this version instead. * `--release-notes ‹semver›` — Print the release body for this version and exit. * `--processing-method ‹method›` — `latest` (default), `all`, or a git range. * `--format-only` — Only regroup existing changelogs. * `--no-package-changes-section` — Skip the package-changes footer. ## `dbx migrate` Migrate Biome / ESLint / Prettier / Dprint / Oxc config, or fold vite.config.\* into dbx.config. * `--from ‹tool›` — Source tool (repeatable): biome | eslint | prettier | dprint | oxc | vite | all. * `--to ‹tool›` — Target tool (default: oxc). (default: `oxc`) * `--cwd ‹dir›` — Project root (default: cwd). * `--recursive` — Also migrate the discovered projects under the root (default: root only). * `--write` — Write the files (default: dry-run). * `--dry-run` — Do not write anything (default). * `--check` — CI: exit non-zero when the oxc config is stale. * `--target ‹target›` — files (default) | vite. * `--online` — Allow network (e.g. @oxlint/migrate). * `--ignore-vite-config-file` — Do not remove the folded `vite.config.*`. * `--ignore-managed-files` — Skip the post-migration sync (leave the toolchain-managed files as-is). * `--ignore-changelog` — Skip the changelog task in the post-migration sync. * `--json` — Print the result as JSON. ## `dbx migrate:list` List the registered migrations and whether each is detected. * `--cwd ‹dir›` — Project root (default: cwd). * `--json` — Print as JSON. ## `dbx project:init` Scaffold a single-package TypeScript project. * `-n, --name ‹name›` — Project name. * `-s, --scope ‹scope›` — npm scope, eg. @acme. * `-o, --out ‹dir›` — Output directory (default: cwd). * `--package-manager ‹pm›` — Package manager: yarn (default) | pnpm | npm | bun. * `--no-config` — Skip the dbx.config.ts file. * `--type ‹type›` — Project type: project | monorepo-root | monorepo-workspace. * `--force` — Overwrite existing files in the output directory. ## `dbx project:init:monorepo` Scaffold a monorepo root (add --example for one example workspace). * `-n, --name ‹name›` — Project name. * `-s, --scope ‹scope›` — npm scope, eg. @acme. * `-o, --out ‹dir›` — Output directory (default: cwd). * `--example` — Also scaffold one example workspace package. * `--package-manager ‹pm›` — Package manager: yarn (default) | pnpm | npm | bun. * `--no-config` — Skip the dbx.config.ts file. * `--type ‹type›` — Project type: project | monorepo-root | monorepo-workspace. * `--force` — Overwrite existing files in the output directory. ## `dbx project:init:workspace` Scaffold a monorepo workspace package into an existing monorepo root. * `-n, --name ‹name›` — Workspace package name (unscoped). * `-s, --scope ‹scope›` — npm scope, eg. @acme. * `-r, --root ‹dir›` — Monorepo root (default: cwd). * `-d, --dir ‹dir›` — Workspace dir relative to the root (default: packages/\). * `--package-manager ‹pm›` — Package manager: yarn (default) | pnpm | npm | bun. * `--no-config` — Skip the dbx.config.ts file. * `--type ‹type›` — Project type: project | monorepo-root | monorepo-workspace. * `--force` — Overwrite existing files in the output directory. ## `dbx project:config` Generate dbx.config.\* for existing projects that are missing one. * `--cwd ‹dir›` — Root to scan (default: cwd). * `--include ‹pattern...›` — Only projects matching these patterns (glob or substring). * `--exclude ‹pattern...›` — Skip projects matching these patterns (glob or substring). * `--type ‹type›` — Force a project type for every config. * `--language ‹language›` — Config language: ts (default) | js. * `--config-extension ‹ext›` — Config extension: ts | mts | js | mjs | cjs (default inferred from the language and package type). * `--skip-config` — Only write .dbx/toolchain/projects.json; do not create dbx.config.\* files. * `--force` — Overwrite configs that already exist. * `--dry-run` — Report what would be written without writing. * `--json` — Print the result as JSON. ## `dbx template:list` List templates (builtin + local + opted-in online sources). * `--cwd ‹dir›` — Project root (default: cwd). * `--online` — Resolve online sources (npm/git). * `--offline` — Force offline (skip npm/git). * `--global` — Only the global registry templates. * `--kind ‹kind›` — project | workspace | application | folder | tooling | pipeline. * `--registry` — List only the .dbx/toolchain/templates.json sources. * `--json` — Print the templates as JSON. ## `dbx template:init` Write a template.json to a directory, making it a local template. * `[dir]` — Directory (default: cwd). * `--cwd ‹dir›` — Project root (default: cwd). * `--kind ‹kind›` — project | workspace | application | folder | tooling | pipeline. * `--name ‹name›` — Display name (default: folder name). * `--id ‹id›` — Stable template id (default: folder name). * `--description ‹text›` — Description. * `--var ‹pair...›` — Variable name\[:default] (repeatable). * `--tag ‹tag...›` — Tag (repeatable). * `--force` — Overwrite an existing template.json. * `--add` — Also record the folder in .dbx/toolchain/templates.json. * `--no-global` — Do not also store the template in the global registry. * `--json` — Print the manifest as JSON. ## `dbx template:add` Record a template source in .dbx/toolchain/templates.json. * `‹source›` — local path | git URL | @scope/create | type:locate * `--cwd ‹dir›` — Project root (default: cwd). * `--type ‹type›` — Force: builtin | local | npm | git | vite-plus | remote. * `--name ‹name›` — Display name. * `--id ‹id›` — Stable template id (default: inferred). * `--kind ‹kind›` — project | workspace | application | folder | tooling | pipeline. * `--ref ‹ref›` — Git branch/tag/commit. * `--registry ‹url›` — Custom npm registry (default: the project package manager). * `--subdir ‹dir›` — Subdirectory within the source. * `--no-global` — Do not also register the source in the global registry. * `--json` — Print the updated registry as JSON. ## `dbx template:remove` Remove a source from .dbx/toolchain/templates.json. * `‹id›` — Source id or type:locate. * `--cwd ‹dir›` — Project root (default: cwd). * `--global` — Remove the template from the global registry. * `--json` — Print the registry as JSON. ## `dbx template:registry` Manage the global template search paths (and sync them to the VS Code setting). * `--list` — List the resolved search paths (default). * `--add ‹path...›` — Add global search path(s). * `--remove ‹path...›` — Remove global search path(s). * `--set ‹path...›` — Replace the global search paths. * `--clear` — Reset the global search paths to the default. * `--vscode-dir ‹dir›` — Record the VS Code user dir (its settings.json holds the paths). * `--json` — Print the resolved config as JSON. ## `dbx registry` List or manage the template/provider registries. * `--cwd ‹dir›` — Project root (default: cwd). * `--kind ‹kind›` — What --add/--remove manages: templates | providers. * `--add ‹path›` — Add a registry path (or remote URL). * `--remove ‹path›` — Remove a registry path. * `--set ‹path...›` — Replace the registry search paths. * `--clear` — Clear the registry search paths. * `--scope ‹scope›` — project | global (default: global). * `--json` — Print the registries as JSON. ## `dbx template:info` Show one template by id. * `‹id›` — The template id. * `--cwd ‹dir›` — Project root (default: cwd). * `--online` — Resolve online sources. * `--json` — Print the template as JSON (the default). ## `dbx create` Scaffold a project from a template (builtin, local or Vite+). * `-t, --template ‹id›` — Template id (see template:list). * `--kind ‹kind›` — project | workspace | application | folder | tooling | pipeline. * `-n, --name ‹name›` — Project / package name. * `-s, --scope ‹scope›` — npm scope, eg. @acme. * `-o, --out ‹dir›` — Output directory (default: cwd). * `--cwd ‹dir›` — Project root (default: cwd). * `--var ‹pair...›` — Template variables as key=value (repeatable). * `--action ‹id...›` — Action id to run after scaffolding (repeatable). * `--online` — Resolve online templates too. * `--force` — Overwrite existing files in the output directory. * `--json` — Print the result as JSON. ## `dbx vite` Run any Vite+ command through `vp` (eg. `dbx vite dev`). * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:create` Vite+: Create a new project from a template. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:migrate` Vite+: Migrate an existing project to Vite+. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:config` Vite+: Configure hooks and agent integration. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:hooks` Vite+: Manage the Git hook dispatcher. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:staged` Vite+: Run linters on staged files. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:install` Vite+: Install dependencies with the detected package manager. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:env` Vite+: Manage Node.js and package managers. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:dev` Vite+: Run the Vite development server. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:check` Vite+: Run format, lint and type checks. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:lint` Vite+: Lint code with the active linter (Oxlint via Vite+). * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:fmt` Vite+: Format code with the active formatter (Oxfmt via Vite+). * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:test` Vite+: Run tests through bundled Vitest. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:run` Vite+: Run package scripts and monorepo tasks. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:exec` Vite+: Execute a binary from local node_modules. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:node` Vite+: Run a Node.js script in the Vite+ env. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:dlx` Vite+: Run a package binary without installing it. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:cache` Vite+: Manage the task cache. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:build` Vite+: Build for production. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:pack` Vite+: Build libraries for npm or standalone binaries. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:preview` Vite+: Preview the production build. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:add` Vite+: Add packages to dependencies. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:remove` Vite+: Remove packages from dependencies. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:update` Vite+: Update packages. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:dedupe` Vite+: Deduplicate dependencies. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:outdated` Vite+: Check for outdated packages. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:list` Vite+: List installed packages. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:why` Vite+: Explain why a package is installed. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:info` Vite+: View package metadata. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:link` Vite+: Link local packages. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:unlink` Vite+: Unlink local packages. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:rebuild` Vite+: Rebuild native modules. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:pm` Vite+: Forward a command to the package manager. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:toolchain` Vite+: Show Vite+ tool versions. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:upgrade` Vite+: Update `vp` itself. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). ## `dbx vite:implode` Vite+: Remove `vp` and all related data. * `--cwd ‹dir›` — Project root to run `vp` in (default: cwd). --- --- url: https://toolchain.deltabox.studio/config/env.md --- # Environment variables The toolchain config is file/CLI backed; a schema leaf opts into an environment variable with `.meta({ env: "..." })` (see the [Config Generator](/guide/config-generator/describe-a-scope)). No environment-backed toolchain settings are declared. --- --- url: https://toolchain.deltabox.studio/config/reference.md --- # dbx.config A `dbx.config.ts` file exports `{ toolchain: { ...sections } }`: the `@deltabox/toolchain` settings live under the `toolchain` key. Every section is **opt-in by presence** (see [Configuration](/guide/config/opt-in-by-presence)): define it to enable it, omit it to leave that area untouched. > Generated from the Zod config schema — do not edit by hand. > Run `yarn config:generate`. ## `project` Project metadata: canonical `package.json` fields and template tokens (shared fields are inherited by workspaces). ### `type` * **Type**: `'monorepo-root' | 'monorepo-workspace' | 'project'` The project kind (detected when omitted). ### `name` * **Type**: `string` The unscoped project name (written as `/`). ### `scope` * **Type**: `string` The npm scope, eg. `@acme`. ### `author` * **Type**: `string` Package author (`package.json` `author`). ### `license` * **Type**: `string` SPDX license id (`package.json` `license`). ### `homepage` * **Type**: `string` Project homepage (`package.json` `homepage`). ### `repo` * **Type**: `string` Repository URL (`package.json` `repository`). For a workspace it also resolves to the workspace path (`repository.directory`). ### `nodeVersion` * **Type**: `string` Node version for CI/templates, eg. `22` (not `package.json` `engines`). ### `packageManager` * **Type**: `string` Package manager name, written as `@` (default `yarn`). ### `packageManagerVersion` * **Type**: `string` Package manager version, eg. `4.17.1`. ### `description` * **Type**: `string` One-line description (`package.json` `description`). ### `inherit` * **Type**: `boolean` Inherit the parent project metadata (default true). Set false to keep this project metadata independent of the root config (other sections still merge normally). ## `ci` GitHub Actions workflows to generate (CI, release, issues, changelog, sync). ### `ci` * **Type**: `WorkflowSetting` The CI checks workflow (`ci.yml`). #### `provider` * **Required** * **Type**: `'github'` Workflow vendor for a full `workflow` object (default `github`). #### `workflow` * **Required** * **Type**: `AzurePipelines` The complete pipeline object. #### `filename` * **Type**: `string` Override the generated workflow file name (eg. `deploy.yml`). #### `dir` * **Type**: `string` Directory for the workflow file (default `.github/workflows`). #### `setup` * **Type**: `boolean` Inject the shared checkout/Node/Corepack/install steps into custom workflow jobs (default false; built-in workflows already include them). #### `enabled` * **Type**: `boolean` Enable/disable this workflow (default: enabled when present). #### `pushBranches` * **Type**: `Array` Replace `on.push.branches`. #### `pullRequestBranches` * **Type**: `Array` Replace `on.pull_request.branches`. #### `triggers` * **Type**: `WorkflowTriggers` Extra `on.*` workflow triggers. ##### `push` * **Type**: `JsonObject` Push trigger (`on.push`). ##### `pull_request` * **Type**: `JsonObject` Pull-request trigger (`on.pull_request`). ##### `pull_request_target` * **Type**: `JsonObject` Pull-request-target trigger (`on.pull_request_target`). ##### `workflow_dispatch` * **Type**: `JsonObject` Manual trigger (`on.workflow_dispatch`). ##### `workflow_call` * **Type**: `JsonObject` Reusable workflow trigger (`on.workflow_call`). ##### `workflow_run` * **Type**: `JsonObject` Triggered by another workflow (`on.workflow_run`). ##### `schedule` * **Type**: `Array` Scheduled trigger list (`on.schedule`). ##### `release` * **Type**: `JsonObject` Release trigger (`on.release`). #### `extra` * **Type**: `WorkflowExtra` Extra top-level workflow keys (`permissions`, `concurrency`, `env`, ...). ##### `permissions` * **Type**: `Record` Workflow permissions. ##### `concurrency` * **Type**: `string | object` Workflow concurrency. ###### `group` * **Required** * **Type**: `string` Concurrency group. ###### `cancel-in-progress` * **Type**: `boolean | string` Cancel in-progress runs (boolean or expression). ##### `env` * **Type**: `Record` Workflow environment variables. ##### `defaults` * **Type**: `object` Default settings for `run` steps. ###### `run` * **Type**: `object` ###### `shell` * **Type**: `string` Default shell. ###### `working-directory` * **Type**: `string` Default working directory. ##### `name` * **Type**: `string` Workflow name. ##### `run-name` * **Type**: `string` Workflow run name. ##### `timeout-minutes` * **Type**: `number` Job timeout in minutes. ##### `container` * **Type**: `JsonObject` Container for the job. ##### `strategy` * **Type**: `JsonObject` Build matrix / strategy. #### `steps` * **Type**: `WorkflowStepsConfig | Array` Extra steps for a job (one group, or per-job groups). ##### `job` * **Type**: `string` Target job id (defaults to the primary/first job). ##### `before` * **Type**: `Array` Steps inserted at the start. ###### `name` * **Type**: `string` Step display name. ###### `id` * **Type**: `string` Step id (for `steps.` outputs). ###### `if` * **Type**: `string` Conditional expression (`if:`). ###### `uses` * **Type**: `string` Action to run (`uses:`), eg. `actions/checkout@v4`. ###### `run` * **Type**: `string` Shell command(s) to run (`run:`). ###### `shell` * **Type**: `string` Shell to run `run` in, eg. `bash`. ###### `working-directory` * **Type**: `string` Directory the step runs in. ###### `with` * **Type**: `Record` Inputs for a `uses` action. ###### `env` * **Type**: `Record` Environment variables for the step. ##### `after` * **Type**: `Array` Steps appended at the end. #### `jobs` * **Type**: `Record` Extra complete jobs merged in. #### `tokens` * **Type**: `WorkflowTokens` Per-workflow token overrides (`nodeVersion`, `packageManager`, ...). ##### `name` * **Type**: `string` Project name (`{{name}}`). ##### `scope` * **Type**: `string` Package scope (`{{scope}}`). ##### `author` * **Type**: `string` Author (`{{author}}`). ##### `license` * **Type**: `string` License (`{{license}}`). ##### `homepage` * **Type**: `string` Homepage (`{{homepage}}`). ##### `repo` * **Type**: `string` Repository URL (`{{repo}}`). ##### `description` * **Type**: `string` Description (`{{description}}`). ##### `nodeVersion` * **Type**: `string` Node version (`{{nodeVersion}}`). ##### `packageManager` * **Type**: `string` Package manager (`{{packageManager}}`). ##### `yarnVersion` * **Type**: `string` Yarn version (`{{yarnVersion}}`). ##### `packageManagerVersion` * **Type**: `string` Package manager version (`{{packageManagerVersion}}`). ##### `mainBranch` * **Type**: `string` Main branch (`{{mainBranch}}`). ##### `devBranch` * **Type**: `string` Dev branch (`{{devBranch}}`). ### `changelog` * **Type**: `ChangelogWorkflowSetting` The changelog/version workflow (`changelog.yml`) plus the changelog pipeline config (versioning, VS Code settings, commitlint). #### `provider` * **Type**: `'github'` Workflow vendor for a full `workflow` object (default `github`). #### `pushBranches` * **Type**: `Array` Replace `on.push.branches`. #### `pullRequestBranches` * **Type**: `Array` Replace `on.pull_request.branches`. #### `triggers` * **Type**: `WorkflowTriggers` Extra `on.*` workflow triggers. #### `extra` * **Type**: `WorkflowExtra` Extra top-level workflow keys (`permissions`, `concurrency`, `env`, ...). #### `steps` * **Type**: `WorkflowStepsConfig | Array` Extra steps for a job (one group, or per-job groups). #### `jobs` * **Type**: `Record` Extra complete jobs merged in. #### `tokens` * **Type**: `WorkflowTokens` Per-workflow token overrides (`nodeVersion`, `packageManager`, ...). #### `filename` * **Type**: `string` Override the generated workflow file name (eg. `deploy.yml`). #### `dir` * **Type**: `string` Directory for the workflow file (default `.github/workflows`). #### `setup` * **Type**: `boolean` Inject the shared checkout/Node/Corepack/install steps into custom workflow jobs (default false; built-in workflows already include them). #### `enabled` * **Type**: `boolean` Enable/disable the changelog workflow and pipeline (default: enabled when present). #### `branches` * **Type**: `ChangelogBranchesConfig` Branches the generated workflows target. ##### `main` * **Type**: `string | Array` Branch glob(s) the CI + release workflows run on, and the release PR base (default `["main"]`). ##### `dev` * **Type**: `string | Array` Branch glob(s) the changelog/version workflow runs on. No default: when omitted it reuses `main` (so a single-branch repo needs no `dev`). #### `commitlint` * **Type**: `boolean | ChangelogCommitlintConfig` commitlint config; set `false` to disable the Conventional-Commits pipeline. ##### `dir` * **Type**: `string` Directory the generated `commitlint.config.mjs` is written to (default: the project root). The husky hook and CI use `--config` to point at it. ##### `types` * **Type**: `Array` Allowed commit types (commitlint). ##### `scopes` * **Type**: `Array` Allowed commit scopes (commitlint). ##### `enabled` * **Type**: `boolean` Enable commitlint (default true when the changelog is enabled). Set false to generate changelogs without the Conventional-Commits pipeline. #### `versioning` * **Type**: `ChangelogVersioning` Automatic version bumping on changelog generation (default `true`). ##### `target` * **Type**: `'uniform' | 'root-only' | 'individual'` `uniform` bumps the root and every workspace to one shared version; `root-only` bumps the root only; `individual` bumps each changed workspace independently (default `uniform`). ##### `workspaces` * **Type**: `Array` Glob patterns selecting the workspaces to version (relative to the root). Defaults to the root `package.json` `workspaces` field; when there is none the root `package.json` is used on its own. ##### `bump` * **Type**: `object` Custom bump rules for major/minor/patch (matchers or callbacks). ###### `major` * **Type**: `ChangelogBumpRule` A bump rule: a matcher/callback or a list of matchers/callbacks. ###### `breaking` * **Type**: `boolean` Match only commits whose breaking state equals this value. ###### `type` * **Type**: `string` Match this Conventional-Commits type. ###### `types` * **Type**: `Array` Match any of these CC types. ###### `scope` * **Type**: `string` Match this Conventional-Commits scope. ###### `scopes` * **Type**: `Array` Match any of these CC scopes. ###### `pattern` * **Type**: `string` Match this regular expression. ###### `match` * **Type**: `string` Match this substring. ###### `minor` * **Type**: `ChangelogBumpRule` A bump rule: a matcher/callback or a list of matchers/callbacks. ###### `patch` * **Type**: `ChangelogBumpRule` A bump rule: a matcher/callback or a list of matchers/callbacks. ##### `allowOverBump` * **Type**: `boolean` Apply a bump once per matching change instead of once per run (default false). ##### `commit` * **Type**: `ChangelogCommitFormatConfig` Commit message formatting/credit options (with an optional formatter callback). ###### `author` * **Type**: `boolean` Include the `by ` credit (default true). ###### `commitSha` * **Type**: `boolean` Include the commit hash link (default true). ###### `issues` * **Type**: `boolean` Include issue/fixes links (default true). ###### `authorAvatar` * **Type**: `boolean` Prefix the author with their GitHub avatar (default false). ###### `format` * **Type**: `((ctx: ChangelogCommitFormatContext) => string)` Fully custom commit line formatter (TS/JS configs only; return the bullet body). ##### `provider` * **Type**: `string | ChangelogProviderConfig | ((ctx: ChangelogProviderContext) => string | void)` `default`, `changesets`, a provider id, or a custom provider/callback. ###### `id` * **Required** * **Type**: `string` The provider id. ###### `run` * **Required** * **Type**: `((ctx: ChangelogProviderContext) => string | void)` A custom provider callback returning the changelog body (TS/JS configs only). #### `vscode` * **Type**: `boolean` Write the Conventional-Commits VS Code settings (default true). #### `groups` * **Type**: `Array | Record | ((ctx: ChangelogGroupsContext) => ChangelogGroupsSetting)` Conventional-Commits group order/titles: an ordered array of CC types or `{ type, title, order, hidden }`, a type->title/options record, or a callback (TS/JS configs only). ##### `type` * **Type**: `string` The CC type (required for object entries in an array). ##### `title` * **Type**: `string | ((ctx: ChangelogGroupContext) => string)` Custom heading title (or callback; TS/JS configs only). ##### `order` * **Type**: `number | ((ctx: ChangelogGroupContext) => number)` Sort position; lower comes first (or callback; TS/JS configs only). ##### `hidden` * **Type**: `boolean | ((ctx: ChangelogGroupContext) => boolean)` Omit this group from the rendered changelog (or callback; TS/JS only). #### `breaking` * **Type**: `boolean | 'conventional' | 'any' | object | ((commit: ChangelogBreakingCommit) => boolean)` How breaking changes are detected: `conventional` (default: a `!` in the CC subject or a `BREAKING CHANGE:` footer), `any` (legacy loose match), `false` (disable), `{ pattern }` / `{ match }`, or a callback (TS/JS configs only). ##### `pattern` * **Type**: `string` A regular expression matched against the subject and body. ##### `match` * **Type**: `string` A substring matched against the subject and body. ### `release` * **Type**: `WorkflowSetting` The release workflow (`release.yml`). ### `issues` * **Type**: `WorkflowSetting` The issue-title workflow (`issues.yml`). ### `sync` * **Type**: `WorkflowSetting` The toolchain sync workflow (`sync.yml`). ### `custom` * **Type**: `Record` Additional custom workflows, keyed by id (eg. `custom.my-workflow`). Each id writes `/.yml`. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `changelog` Manual `toolchain.changelog` pipeline config for `dbx changelog`, the VS Code actions and the API; `ci.changelog` inherits these options. ### `commitlint` * **Type**: `boolean | ChangelogCommitlintConfig` commitlint config; set `false` to disable the Conventional-Commits pipeline. #### `dir` * **Type**: `string` Directory the generated `commitlint.config.mjs` is written to (default: the project root). The husky hook and CI use `--config` to point at it. #### `types` * **Type**: `Array` Allowed commit types (commitlint). #### `scopes` * **Type**: `Array` Allowed commit scopes (commitlint). #### `enabled` * **Type**: `boolean` Enable commitlint (default true when the changelog is enabled). Set false to generate changelogs without the Conventional-Commits pipeline. ### `versioning` * **Type**: `ChangelogVersioning` Automatic version bumping on changelog generation (default `true`). #### `target` * **Type**: `'uniform' | 'root-only' | 'individual'` `uniform` bumps the root and every workspace to one shared version; `root-only` bumps the root only; `individual` bumps each changed workspace independently (default `uniform`). #### `workspaces` * **Type**: `Array` Glob patterns selecting the workspaces to version (relative to the root). Defaults to the root `package.json` `workspaces` field; when there is none the root `package.json` is used on its own. #### `bump` * **Type**: `object` Custom bump rules for major/minor/patch (matchers or callbacks). ##### `major` * **Type**: `ChangelogBumpRule` A bump rule: a matcher/callback or a list of matchers/callbacks. ###### `breaking` * **Type**: `boolean` Match only commits whose breaking state equals this value. ###### `type` * **Type**: `string` Match this Conventional-Commits type. ###### `types` * **Type**: `Array` Match any of these CC types. ###### `scope` * **Type**: `string` Match this Conventional-Commits scope. ###### `scopes` * **Type**: `Array` Match any of these CC scopes. ###### `pattern` * **Type**: `string` Match this regular expression. ###### `match` * **Type**: `string` Match this substring. ##### `minor` * **Type**: `ChangelogBumpRule` A bump rule: a matcher/callback or a list of matchers/callbacks. ##### `patch` * **Type**: `ChangelogBumpRule` A bump rule: a matcher/callback or a list of matchers/callbacks. #### `allowOverBump` * **Type**: `boolean` Apply a bump once per matching change instead of once per run (default false). #### `commit` * **Type**: `ChangelogCommitFormatConfig` Commit message formatting/credit options (with an optional formatter callback). ##### `author` * **Type**: `boolean` Include the `by ` credit (default true). ##### `commitSha` * **Type**: `boolean` Include the commit hash link (default true). ##### `issues` * **Type**: `boolean` Include issue/fixes links (default true). ##### `authorAvatar` * **Type**: `boolean` Prefix the author with their GitHub avatar (default false). ##### `format` * **Type**: `((ctx: ChangelogCommitFormatContext) => string)` Fully custom commit line formatter (TS/JS configs only; return the bullet body). #### `provider` * **Type**: `string | ChangelogProviderConfig | ((ctx: ChangelogProviderContext) => string | void)` `default`, `changesets`, a provider id, or a custom provider/callback. ##### `id` * **Required** * **Type**: `string` The provider id. ##### `run` * **Required** * **Type**: `((ctx: ChangelogProviderContext) => string | void)` A custom provider callback returning the changelog body (TS/JS configs only). ### `vscode` * **Type**: `boolean` Write the Conventional-Commits VS Code settings (default true). ### `groups` * **Type**: `Array | Record | ((ctx: ChangelogGroupsContext) => ChangelogGroupsSetting)` Conventional-Commits group order/titles: an ordered array of CC types or `{ type, title, order, hidden }`, a type->title/options record, or a callback (TS/JS configs only). #### `type` * **Type**: `string` The CC type (required for object entries in an array). #### `title` * **Type**: `string | ((ctx: ChangelogGroupContext) => string)` Custom heading title (or callback; TS/JS configs only). #### `order` * **Type**: `number | ((ctx: ChangelogGroupContext) => number)` Sort position; lower comes first (or callback; TS/JS configs only). #### `hidden` * **Type**: `boolean | ((ctx: ChangelogGroupContext) => boolean)` Omit this group from the rendered changelog (or callback; TS/JS only). ### `breaking` * **Type**: `boolean | 'conventional' | 'any' | object | ((commit: ChangelogBreakingCommit) => boolean)` How breaking changes are detected: `conventional` (default: a `!` in the CC subject or a `BREAKING CHANGE:` footer), `any` (legacy loose match), `false` (disable), `{ pattern }` / `{ match }`, or a callback (TS/JS configs only). #### `pattern` * **Type**: `string` A regular expression matched against the subject and body. #### `match` * **Type**: `string` A substring matched against the subject and body. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `fmt` Formatter configuration: Oxc/Biome configs, editor defaults and source-tool options. ### `vscode` * **Type**: `boolean | ToolchainVscodeSettings` Add project-scoped VS Code settings: `true`/`false` or `{ overrides, exclude }` (default false; existing keys are kept). #### `overrides` * **Type**: `JsonObject` Settings deep-merged over the generated editor defaults. #### `exclude` * **Type**: `Array` Exact setting keys (eg. `editor.defaultFormatter`) to omit. ### `oxc` * **Type**: `boolean | OxfmtConfig` Oxfmt options (`true` = defaults, `false` = disabled). `toolchain.vite.fmt` overrides these (deep merge). ### `biome` * **Type**: `boolean | BiomeConfig` Biome formatter options (`true` = defaults, `false` = disabled); merged into the generated `biome.json` (arrays union; `$remove:`/`$reset` prune or replace them). ### `prettier` * **Type**: `boolean | PrettierConfig` Prettier options (`true` = defaults, `false` = disabled). ### `dprint` * **Type**: `boolean | DprintConfig` Dprint options (`true` = defaults, `false` = disabled). ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `lint` Linter configuration: Oxc/Biome configs, editor defaults and ESLint options. ### `vscode` * **Type**: `boolean | ToolchainVscodeSettings` Add project-scoped VS Code settings: `true`/`false` or `{ overrides, exclude }` (default false; existing keys are kept). #### `overrides` * **Type**: `JsonObject` Settings deep-merged over the generated editor defaults. #### `exclude` * **Type**: `Array` Exact setting keys (eg. `editor.defaultFormatter`) to omit. ### `oxc` * **Type**: `boolean | OxlintConfig` Oxlint options (`true` = defaults, `false` = disabled). `toolchain.vite.lint` overrides these (deep merge). ### `biome` * **Type**: `boolean | BiomeConfig` Biome linter options (`true` = defaults, `false` = disabled); merged into the generated `biome.json` (arrays union; `$remove:`/`$reset` prune or replace them). ### `eslint` * **Type**: `boolean | EslintConfig` ESLint options (`true` = defaults, `false` = disabled). ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `biome` Biome config generation (for consumers that still want a `biome.json`). * **Deprecated**: use `toolchain.fmt.biome` / `toolchain.lint.biome`. Still writes `biome.json`. ### `vscode` * **Type**: `boolean` Write the Biome VS Code settings (default true). ### `overrides` * **Type**: `BiomeConfig` Deep-merged over the generated `biome.json` (arrays union; `$remove:`/`$reset` prune or replace them). ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `tsconfig` Workspace tsconfig generation (path aliases + project references). ### `glob` * **Type**: `string | Array` Glob(s) for the workspace default tsconfig files. Defaults to matching every `defaultFile` recursively. ### `exclude` * **Type**: `string | Array` Glob(s) to exclude from `glob`. Defaults to the common ignored paths (`node_modules`, `.dbx`, `dist`, `build`, ...). ### `gitignore` * **Type**: `boolean` Also respect `.gitignore` files (root + nested) when matching `glob` (default true). ### `refs` * **Type**: `boolean` Wire project references (default false). ### `composite` * **Type**: `boolean` Emit `composite: true` (default false). ### `aliases` * **Type**: `boolean` Inject path aliases (default true). ### `auto` * **Type**: `boolean | ToolchainTsconfigAliasAutoSetting` Auto-generate the root workspaces tsconfig paths/references from the project graph (default false). Set true to derive them from the project graph; set false to only use the entries you define yourself. #### `include` * **Type**: `Array` Only auto-wire entries matching these globs (`auto`: workspace dirs; `aliasAuto`: rewritten alias keys; `refsAuto`: reference paths). #### `exclude` * **Type**: `Array` Skip entries matching these globs (same targets as `include`). #### `rewriteAlias` * **Type**: `ToolchainTsconfigAliasRewrite` Rewrite the alias keys auto-wiring generates (custom keys are preserved). Patterns are URL-rewrite style: a string is a regular-expression source and replacements support `$1`, `$2`, ... capture groups. Examples: `{ "^@acme/(.+)$": "@acme/pkg/$1" }` (string patterns), `[{ pattern: /^@acme\/(.+)$/, replacement: "@acme/pkg/$1" }]` (RegExp patterns), `[{ pattern: ["^@acme/a$", "^@acme/b$"], replacement: "@acme/x" }]` (an array of keys), and an array of such rules (applied in order; the first matching rule wins). `RegExp` patterns are only available from TS/JS configs; JSON configs use string patterns. ### `aliasAuto` * **Type**: `boolean | ToolchainTsconfigAliasAutoSetting` Auto-generate path aliases (defaults to `auto`; `aliases` must also be enabled). Accepts the same `include`/`exclude`/`rewriteAlias` object as `auto`. ### `refsAuto` * **Type**: `boolean | ToolchainTsconfigAutoSetting` Auto-generate project references (defaults to `auto`; `refs` must also be enabled). #### `include` * **Type**: `Array` Only auto-wire entries matching these globs (`auto`: workspace dirs; `aliasAuto`: rewritten alias keys; `refsAuto`: reference paths). #### `exclude` * **Type**: `Array` Skip entries matching these globs (same targets as `include`). ### `autoSrcDir` * **Type**: `'src' | '.' | (string & {})` Source directory used to resolve workspace entry points when `package.json` does not declare `exports`/`main`/`types` (default: the workspace's final tsconfig `rootDir`/`baseUrl`, falling back to `src`). ### `indent` * **Type**: `number` JSON indentation (default 4). ### `rootFile` * **Type**: `string` The workspaces tsconfig file name that holds `paths` + `references` (default `tsconfig.workspaces.json`). ### `defaultFile` * **Type**: `string` The workspace default tsconfig file name that the generator expands (default `tsconfig.default.json`). ### `buildFile` * **Type**: `string` The workspace build tsconfig file name (default `tsconfig.build.json`). ### `baseFile` * **Type**: `string` The root base tsconfig file name (default `tsconfig.base.json`). ### `extends` * **Type**: `object` Per-file `extends` overrides for generated/scaffolded tsconfig templates. #### `base` * **Type**: `string | Array | false` `extends` written into scaffolded `tsconfig.base.json` files (or `false` to remove the field). #### `default` * **Type**: `string | Array | false` `extends` written into scaffolded `tsconfig.default.json` files (or `false` to remove the field). #### `build` * **Type**: `string | Array | false` `extends` written into scaffolded `tsconfig.build.json` files. Default `./tsconfig.default.json`; use `./tsconfig.json` to let builds reuse the path aliases (at the cost of pulling workspace source into the build). ### `overrides` * **Type**: `ToolchainTsconfigOverrides` Managed overrides deep-merged into the base/root/default/build tsconfig files during sync. The originals are snapshotted in the manifest, so removing a key reverts it (restoring the original value when the file defined one). #### `base` * **Type**: `TsConfigJson` Deep-merged into the root base tsconfig (`baseFile`) during sync. #### `default` * **Type**: `TsConfigJson` Deep-merged into every workspace default tsconfig (`defaultFile`) during sync. #### `build` * **Type**: `TsConfigJson` Deep-merged into every workspace build tsconfig (`buildFile`) during sync. #### `root` * **Type**: `TsConfigJson` Deep-merged into the root workspaces tsconfig (`rootFile`) during sync. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `templates` Template sources / scaffolding. ### `sources` * **Type**: `Array` Extra sources on top of the builtin + registry sources. #### `type` * **Required** * **Type**: `'builtin' | 'local' | 'npm' | 'git' | 'vite-plus' | 'remote'` How to load the source. #### `locate` * **Required** * **Type**: `string` A builtin id, local dir, npm scope, git URL or Vite+ id. #### `id` * **Type**: `string` Stable id (defaults to the basename/scope). #### `name` * **Type**: `string` Display name override. #### `description` * **Type**: `string` Description override. #### `kind` * **Type**: `'project' | 'workspace' | 'application' | 'folder' | 'tooling' | 'pipeline'` What the template produces. #### `ref` * **Type**: `string` Git branch/tag/commit. #### `registry` * **Type**: `string` Custom npm registry URL (default: the project package manager registry). #### `subdir` * **Type**: `string` Subdirectory within the source that holds the template. #### `tags` * **Type**: `Array` Free-form tags. #### `online` * **Type**: `boolean` Whether the source needs network access. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `registry` Registry source allow/deny policies (templates + providers). ### `templates` * **Type**: `ToolchainRegistryPolicy` Allow/deny globs for template sources and registries. #### `allow` * **Type**: `Array` Allowed host/path globs. When non-empty, only matching sources are used (eg. `*.example.com`, `https://registry.example.com/**`). #### `deny` * **Type**: `Array` Denied host/path globs (checked before `allow`). ### `providers` * **Type**: `ToolchainRegistryPolicy` Allow/deny globs for provider sources and registries. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `catalogs` Package catalogs written to the package manager catalog file (`catalog:`). ### `packages` * **Type**: `Record` The default catalog: package name -> version range. ### `named` * **Type**: `Record>` Named catalogs (Yarn `catalogs:` / pnpm `catalogs:`): name -> packages. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `vite` The full Vite+ config; `dbx sync` writes the root `vite.config.ts` from it. ### `config` * **Type**: `boolean | string` Write the managed Vite config (default true). A string sets its file name/path (relative to the project, or an absolute path inside it). ### `shim` * **Type**: `'runtime' | 'static'` How the generated `vite.config.ts` is rendered. `runtime` (default) loads the config from `dbx.config.*`; `static` serializes it into the file so the shim does not import `@deltabox/toolchain` (it then needs no built `dist`). ### `shimImport` * **Type**: `string` Module specifier used to import `resolveVite` in runtime shims (default `@deltabox/toolchain/vite`). A relative path is resolved from the repo root and rewritten relative to each generated config - useful for dogfooding a source checkout without a built `dist`. ### `fmt` * **Type**: `boolean | OxfmtConfig` Oxfmt options; `true` uses the toolchain defaults. ### `lint` * **Type**: `boolean | OxlintConfig` Oxlint options; `true` uses the toolchain defaults. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `watch` Watch behaviour for `dbx watch`. ### `debounce` * **Type**: `number` Debounce window in ms (default 150). ### `exclude` * **Type**: `string | Array` Glob(s) excluded from watching. Defaults to the common ignored paths (`node_modules`, `.dbx`, `dist`, `build`, ...). ### `gitignore` * **Type**: `boolean` Also respect `.gitignore` files (root + nested) when watching (default true). ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `logger` Logging level, colours and prefix for toolchain output. ### `level` * **Type**: `'debug' | 'info' | 'warn' | 'error' | 'silent'` Minimum level to print (default `info`). ### `color` * **Type**: `boolean` Force colours on/off (default: on; `--no-color` disables). ### `name` * **Type**: `string` Prefix non-indented lines with `:` (default `dbx`). ### `suppress` * **Type**: `string | Array` Warning/error id, message substring, or `/regex/` to silence (single value or array). ### `logger` * **Type**: `object` Custom `ToolchainLogger` implementation (TS/JS configs only; JSON cannot carry functions). #### `raw` * **Required** * **Type**: `(message?: string) => void` Write raw stdout. #### `rawError` * **Required** * **Type**: `(message?: string) => void` Write raw stderr. #### `write` * **Required** * **Type**: `(channel: 'out' | 'err', text: string) => void` Write to a specific output channel. #### `debug` * **Required** * **Type**: `(message?: string) => void` Print a debug message. #### `info` * **Required** * **Type**: `(message?: string) => void` Print an info message. #### `success` * **Required** * **Type**: `(message?: string) => void` Print a success message. #### `warn` * **Required** * **Type**: `(message?: string, id?: string) => void` Print a warning. #### `error` * **Required** * **Type**: `(message?: string, id?: string) => void` Print an error. ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## `providers` Provider command behaviour (eg. colour for the passthrough commands). ### `color` * **Type**: `boolean` Force provider command colours on/off (default: inherit the logger). ### `enabled` * **Type**: `boolean` Enable/disable this section (default: enabled when defined). ## Full example ```ts [dbx.config.ts] import { defineConfig } from '@deltabox/toolchain'; export default defineConfig({ toolchain: { project: { type: 'monorepo-root', name: 'acme', scope: '@acme' }, ci: { ci: true, release: true, changelog: { branches: { main: 'main' } } }, tsconfig: { refs: true }, templates: { sources: [{ type: 'local', locate: './templates', kind: 'application' }] }, vite: { fmt: { semi: true, singleQuote: true }, test: { include: ['src/**/*.test.ts'] } }, watch: { debounce: 150 }, }, }); ``` --- --- url: https://toolchain.deltabox.studio/guide.md --- # Getting Started `@deltabox/toolchain` is an all-in-one project toolchain. You drop a `dbx.config.ts` at a repo root and the toolchain generates and manages your configs, tsconfigs, changelogs, CI and more — from the CLI, the programmatic API and the VS Code extension. ## Install ::: code-group ```sh [npm] # as a dev dependency in the project you want to manage npm install @deltabox/toolchain --save-dev ``` ```sh [yarn] # as a dev dependency in the project you want to manage yarn add -D @deltabox/toolchain ``` ```sh [pnpm] # as a dev dependency in the project you want to manage pnpm add -D @deltabox/toolchain ``` ```sh [bun] # as a dev dependency in the project you want to manage bun add -D @deltabox/toolchain ``` ::: ```sh # or for global usage npm install -g @deltabox/toolchain ``` The published package exposes one binary, `dbx`: ```sh dbx --help ``` ## Interactive CLI Commands are flag-driven, but in an interactive terminal (a TTY) a few prompt for the values you omit: ```sh dbx create # pick a template, project name, scope, inputs and actions dbx migrate --recursive # pick which projects to migrate ``` `dbx migrate` without `--from` still detects the sources automatically; the prompt is only for the project selection with `--recursive`. Pass `--json`, pipe the command, or supply the flags to run non-interactively. `dbx template:init` also accepts flags (`--name`, `--kind`, ...) for scripted use. ## Create a config Scaffold a config for a project that does not have one yet: ```sh dbx project:config # detect + write dbx.config.ts ``` Or start from a template: ```sh dbx template:list dbx create --template project --name my-app --out ./my-app ``` ## Sync `sync` runs every enabled task and writes the managed files (adding a manifest at `.dbx/toolchain/manifest.json`): ```sh dbx sync # write managed files dbx sync --check # CI: fail if files are out of date dbx sync --recursive # every project with its own dbx.config.*, in dependency order dbx sync --only tsconfig # only refresh tsconfig aliases/references dbx watch # re-sync on change ``` The generated state lives under `.dbx/`. Add it to `.gitignore`, keeping only the manifest (it holds no absolute paths): ```gitignore .dbx/* !.dbx/toolchain .dbx/toolchain/* !.dbx/toolchain/manifest.json ``` `dbx sync` adds this block to an existing `.gitignore` automatically, so you only need to add it by hand when the repo has no `.gitignore` yet. ## What gets managed | Area | Section in the config | | ------------------------------------------------------------------- | ----------------------------- | | GitHub workflows, commitlint, Conventional-Commits VS Code settings | `ci` | | `biome.json` (formatter/linter) | `fmt.biome` / `lint.biome` | | `.oxfmtrc.json` / `.oxlintrc.json` (Oxc) | `fmt.oxc` / `lint.oxc` | | Workspace `tsconfig.json` files (aliases + references) | `tsconfig` | | `vite.config.ts` (Vite+ consumers) | `vite` | | Template sources / scaffolding | `templates` | | Package catalogs (`.yarnrc.yml` / `pnpm-workspace.yaml`) | [`catalogs`](/guide/catalogs) | | Watch behaviour | `watch` | Every section is **opt-in by presence**: define it to enable it, omit it to leave that area untouched. ## Next * [Monorepo guide](/guide/monorepo) * [CLI reference](/guide/cli) * [Configuration reference](/config/reference) * [TypeScript](/guide/typescript) * [Project](/guide/project) * [Package catalogs](/guide/catalogs) * [CI](/guide/ci/) * [Changelog](/guide/changelog/) * [tsconfig](/guide/tsconfig) * [Formatting & linting](/guide/formatting) * [Vite+](/guide/vite) * [Watch](/guide/watch) * [Logger](/guide/logger) * [Config generator](/guide/config-generator/) * [Providers (Vite+)](/guide/providers) * [Package scripts](/guide/scripts) * [Templates](/guide/templates) * [Migrations](/guide/migrations/) * [Programmatic API](/guide/api/) * [LLMs](/llms) --- --- url: https://toolchain.deltabox.studio/guide/monorepo.md --- # Monorepo Guide ## Project graph `dbx projects` discovers every workspace package and writes a dependency graph to `.dbx/toolchain/projects.json`. The VS Code extension and the `sync --recursive` command consume it. ## tsconfig generation Enable the `tsconfig` section and the toolchain generates each workspace's `tsconfig.json` (extending `tsconfig.default.json`) and, for a monorepo root, a `tsconfig.workspaces.json` wiring project references and path aliases: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { project: { type: 'monorepo-root', name: 'acme', scope: '@acme' }, tsconfig: { refs: true, aliases: true, indent: 4 }, }, }); ``` See the [tsconfig guide](/guide/tsconfig) for every option and why the toolchain splits the config across several files. ## Scaffolding a monorepo ```sh dbx project:init:monorepo --name acme --scope @acme --out . dbx project:init:workspace --name utils --scope @acme --root . --dir packages/utils ``` ## Tasks `deltabox toolchain` (and Vite+ consumers) use **Vite Task** (`dbx run`) instead of Turbo. The package graph orders execution via each package's `package.json` dependencies. Each package declares `build`, `typecheck` and `test` in its own `dbx.config.*` with explicit cache input/output, so `dbx run` reports cache hits (a `package.json` script cannot carry cache config): ```ts [packages//dbx.config.ts] import { defineConfig } from '@deltabox/toolchain'; export default defineConfig({ toolchain: { vite: { run: { tasks: { build: { command: 'tsdown --config-loader unrun', cache: { input: [{ auto: true }, '!dist/**', '!**/*.tsbuildinfo'], output: ['dist/**'], }, }, typecheck: { command: 'tsc -p tsconfig.build.json --noEmit', cache: { input: [{ auto: true }, '!**/*.tsbuildinfo'], output: [] }, }, test: { command: 'vitest run', cache: { input: [{ auto: true }, '!**/*.tsbuildinfo'], output: [] }, }, }, }, }, }, }); ``` The root `package.json` orchestrates them and adds the umbrella build: ```json [package.json] { "scripts": { "build": "dbx run --filter \"./packages/*\" build && dbx run build:root", "test": "dbx run --filter \"./packages/*\" test && dbx run test:root" } } ``` `dbx run --filter "./packages/*" build` runs each package's `build` in dependency order (excluding the root, which avoids recursion), then `build:root` builds the umbrella dist. --- --- url: https://toolchain.deltabox.studio/guide/vscode.md --- # VS Code Extension The `deltabox toolchain` extension provides a sidebar for managing projects. ## Install Download from the [Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=deltabox.toolchain-vscode). or through the CLI: ```sh code --install-extension deltabox.toolchain-vscode ``` ## Commands | Command | What it does | | ---------------------------- | ---------------------------------------------------------------------------------------------- | | Sync all / active / selected | Run `dbx sync` (`--recursive` for config-bearing projects, then configless ones individually). | | Check sync | `sync --check` across the workspace. | | Sync tsconfig | Run `dbx sync --only tsconfig` (refresh aliases/references after builds). | | Manage tsconfig… | Auto-wire the root workspaces tsconfig paths/references (`dbx tsconfig`). | | Scaffold project | Single project, monorepo root (with/without example), workspace, or **From template…**. | | Add template… | `template:init --add` on a folder you pick; asks whether to register it globally. | | Remove template… | `template:list --registry` then `template:remove`. | | Generate configs | `project:config` for missing configs, or refresh the project graph only; picks TS/JS. | | Generate config | Per-project `project:config` (also works from the Command Palette). | | Migrate tooling… | Migrate Biome/Prettier/Dprint/ESLint/Oxc/vite config to the toolchain (`dbx migrate`). | | Run workflow | Trigger generated workflows with their vendor CLI (`gh`/`glab`/`az`). | | Run Vite+ command… | QuickPick over the Vite+ provider commands, run in a terminal. | | Create Vite+ project… | `vp create` in a terminal. | | Add provider... | Register a custom provider (project or global registry). | | Run command... | Run a provider command (`dbx `) in a terminal. | | Create from template... | `dbx create --template ` (terminal for online templates). | | Reset tooling | Remove generated files / manifest / config. | | Toggle auto-sync | Enable/disable sync-on-change. | Interactive commands (the dev server, `vp create`, online scaffolding) run in an integrated terminal at the project root; everything else runs in the output channel. ## Project menu Every project card (and every project in the **Projects** tree) exposes the same actions, scoped to that project: * **Sync** / **Check sync** — regenerate / verify the project's managed files. * **Generate config** — write a `dbx.config.*` (TS/JS), or refresh only the project graph. * **Run workflow…** — trigger one of the project's generated workflows. The picker lists every enabled workflow with its provider and path, and dispatches it through the matching vendor CLI (`gh`, `glab` or `az`). * **Run Vite+ command…** — run a `vp` command in that project. * **Reset tooling…** — remove the generated files + manifest, or everything including the config. * **Go to project** — reveal the project in the Explorer. ## Providers, templates and Vite+ in the editor The **Providers** and **Templates** views read `dbx providers --json` and `dbx template:list --json`, so adding one surfaces it in the UI automatically. ### Add provider... Writes a custom provider to the default project registry (`.dbx/toolchain/.registry/providers/providers.json`) or the global `~/.dbx/toolchain/.registry/providers/` when you choose the global scope, which `toolchainProviders()` merges with the built-ins, so a registered provider's commands also become `dbx` commands (`dbx `), runnable from the view. Provider search paths can also be added with the **Registries** view; the `deltabox.providerRegistrySearchPaths` setting mirrors them globally. ### Add template... Runs `template:init --add` and asks whether to add the template to the **global** registry (yes by default), so it is available in every project. See [Global templates](/guide/templates#global-templates). The `deltabox.registrySearchPaths` setting controls the global search paths and is kept in sync with the CLI config. ### Remove template source... Runs `template:list --registry` + `template:remove`. The **Registries** view (below **Templates**) shows the detected provider and template registries as **Providers** / **Templates** roots. Each item has **Open registry** (the folder in the explorer, or the website for remote URLs), **Add new registry** and **Remove registry** (it only drops the path, keeping the files). The title bar's **New registry…** / **Remove registry…** actions add or remove search paths for either kind, asking whether they are global or project-scoped. Remote registries are `http(s)` JSON registries: template registries accept `{ sources }` or an array of sources; provider registries accept `{ providers }` or an array of provider descriptors. ### Create from template... Prompts for the template's inputs and actions (see [Templates](/guide/templates)) before running `dbx create`. Scaffolding also asks for a package manager (`yarn` default). The Home view links to this documentation with **Need help? Go to documentation**. ## Auto-sync Auto-sync watches input files only. The extension asks the CLI (`dbx watch --list --json`) for the concrete, project-graph-resolved files: every `dbx.config.*` plus each enabled project's `tsconfig.baseFile`, `tsconfig.defaultFile` and the root `tsconfig.rootFile`. Custom file names are honoured, nested configs can disable or override their own tsconfig section, and the list is refreshed whenever a `dbx.config.*` changes (and after each sync). Each resolved pattern is scoped to its own workspace root (and a concrete nested file is watched from its own directory), so a same-named file in another workspace folder or an ancestor can never trigger this root's sync, and VS Code does not install a recursive watcher over the whole root (which can hold `node_modules` handles during a package-manager link step on Windows). Only the root whose input changed is synced. Setting `deltabox.toolchain.watch` explicitly replaces the resolved inputs; user globs are matched across every workspace folder, absolute/ancestor globs are ignored, and an empty list disables watching. Generated outputs are never watched. The scheduler debounces bursts, allows one sync at a time, queues at most one follow-up, and suppresses events briefly after a run. When a sync finds no workspace tsconfig files while `tsconfig.refs`/`tsconfig.composite` are on (or a `tsconfig.workspaces.json` is present), the extension surfaces the CLI's warning as a notification (once per session) with a **Show output** action. When removing a section stops managing a file that dbx created, the sync notification lists the removed relative paths and offers **Restore** (write the previous content back) or **Close**. A file that existed before dbx first managed it is **adopted**: it is unmanaged but never deleted. The **Managed Files** view also shows a **Workflow conflicts** group for hand-written workflows dbx left untouched (the manifest's `workflowConflicts`). Each item names the config key to opt out with, and opens the existing file. ## Settings | Setting | Default | Description | | ---------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------- | | `deltabox.toolchain.enabled` | `true` | Auto sync on watched changes. | | `deltabox.toolchain.debounce` | `300` | Debounce (ms) before a sync. | | `deltabox.toolchain.command` | `sync` | The command to run on change. | | `deltabox.toolchain.enableFormatImportGroupDefaults` | `true` | Add the resolved workspace `sortImports` custom group when syncing; set `false` to manage groups yourself. | | `deltabox.toolchain.watch` | `[]` (resolved) | Inputs that trigger a sync (empty = resolved from the config). | | `deltabox.toolchain.version` | `""` (auto) | Pin the `@deltabox/toolchain` version this workspace runs. | | `deltabox.registrySearchPaths` | `[]` | Template registry search paths (dirs or `http(s)` URLs). | | `deltabox.providerRegistrySearchPaths` | `[]` | Provider registry search paths (JSON files/dirs/URLs). | `deltabox.toolchain.enableFormatImportGroupDefaults` (default `true`) adds the resolved `workspace` group to Oxfmt's `sortImports.customGroups` when the extension syncs or generates configs. Set it to `false` in user or workspace settings to keep full control of the import groups. The extension passes the effective value to the CLI; direct CLI runs and Vite+ configs also honour the workspace-scoped `.vscode/settings.json` value. When a workspace has its own `@deltabox/toolchain` whose version differs from the extension's bundled CLI, the extension asks which to run and shows both detected versions. **Select dbx version…** pins a version in the project's `.vscode/settings.json` (like TypeScript's version picker); it runs that version via `npx` unless the project already installs it. ## Command palette Search for `deltabox:` to find: * **Sync active project** * **Sync selected projects** * **Sync all projects** * **Check sync** * **Manage tsconfig…** * **Scaffold project…** * **Add template…** * **Remove template…** * **Add provider...** * **Refresh providers** * **Add template...** * **Remove template source...** * **Refresh templates** * **Generate configs…** * **Migrate tooling…** * **Run workflow…** * **Run Vite+ command…** * **Run command...** * **Create Vite+ project…** * **Choose dbx source…** * **Reset tooling…** * **Refresh projects** * **Refresh tracked files** * **Select dbx version…** * **New registry…** * **Remove registry…** * **Toggle auto-sync** --- --- url: https://toolchain.deltabox.studio/llms.md --- # LLMs This documentation is published in machine-readable forms so LLMs and coding agents can read it efficiently, following the [llms.txt](https://llmstxt.org) standard: * [`/llms.txt`](/llms.txt) — a concise, link-first overview: a short description, how the toolchain works, and a table of contents linking to the Markdown version of every page. Small enough to fit in a context window. * [`/llms-full.txt`](/llms-full.txt) — the **entire** documentation concatenated into a single Markdown file, for when an agent wants everything in one fetch. Every page is also available as raw Markdown by appending `.md` to its URL (eg. `/guide/tsconfig.md`). ## When the site is unavailable If `https://toolchain.deltabox.studio` is down, the same documentation lives in the repository at [github.com/deltabox-studio/toolchain/tree/main/apps/docs](https://github.com/deltabox-studio/toolchain/tree/main/apps/docs). --- --- url: https://toolchain.deltabox.studio/guide/api.md --- # Programmatic API Every surface the CLI and the extension use is also importable. The umbrella package re-exports the config model + sync runner and every workspace package, so a consumer can import the whole surface from the root or a single package through a subpath. ## Where to start * [Umbrella (`@deltabox/toolchain`)](/guide/api/umbrella) — the root exports and the namespaced packages. * [Subpath exports](/guide/api/subpath-exports) — the per-package entry points. * [Package reference](/guide/api/package-reference) — every package's public exports. * [Logging](/guide/api/logging) — the shared logger. * [Examples](/guide/api/examples) — short, copy-pasteable recipes. * [Registering commands](/guide/api/registering-commands) — wire `ToolchainCommand[]` onto a commander program. ```ts import { defineConfig, loadToolchainConfig, runSync, defaultSyncTasks } from '@deltabox/toolchain'; const { config, tokens, projectType } = await loadToolchainConfig(process.cwd()); await runSync({ tasks: defaultSyncTasks(), cwd: process.cwd(), config, tokens, projectType }); ``` --- --- url: https://toolchain.deltabox.studio/guide/api/examples.md --- # Examples ```ts // Vite+ provider import { resolveVp, runVp, resolveVpCommand, VP_COMMANDS } from '@deltabox/toolchain/vite'; await runVp(['check'], { cwd: process.cwd() }); resolveVpCommand('i'); // 'install' // Migrations import { detectMigrations, runMigration } from '@deltabox/toolchain/migrate'; for (const m of detectMigrations(process.cwd())) await m.run({ cwd: process.cwd(), write: true }); // Templates import { loadTemplates, scaffoldTemplate } from '@deltabox/toolchain/template'; const [template] = await loadTemplates({ cwd: process.cwd() }); if (template) await scaffoldTemplate(template, { cwd: './out', tokens: { name: 'acme' } }); // Providers (what the editor reads) import { toolchainProviders } from '@deltabox/toolchain/providers'; console.log(toolchainProviders().map(p => p.id)); // Sync only the tsconfig task (eg. after a build changed package exports) import { defaultSyncTasks, loadToolchainConfig, runSync } from '@deltabox/toolchain'; // Config engine import { createConfigEngine } from '@deltabox/toolchain/config'; const loaded = await loadToolchainConfig(process.cwd()); await runSync({ tasks: defaultSyncTasks(), only: ['tsconfig'], cwd: process.cwd(), config: loaded.config, tokens: loaded.tokens, projectType: loaded.projectType, }); ``` --- --- url: https://toolchain.deltabox.studio/guide/api/logging.md --- # Logging Every toolchain message goes through one level-aware logger, rendered with [`@clack/prompts`](https://bombshell.dev/clack) (level symbols + guide). Use it instead of `console.log` so output stays consistent and respects the config and the CLI flags. | Export / member | Description | | ------------------------------------------------- | -------------------------------------------------------------------------------------------- | | `logger` | The active singleton: `raw`, `debug`, `info`, `success`, `warn`, `error`, `level`, `colors`. | | `createLogger(options)` | Create an independent logger (`level`, `color`, `name`). | | `setLogConfig(section)` | Apply the `logger` section from the loaded config. | | `setLogOverrides(options)` | Apply CLI overrides (win over the config). `configureLogger` is an alias. | | `getLogOverrides()` / `getLogger()` | Read the current overrides / the active instance. | | `LOG_LEVELS` / `isLogLevel` / `normalizeLogLevel` | The valid levels, most verbose first (`debug`, `info`, `warn`, `error`, `silent`). | ```ts import { createLogger, logger, setLogConfig, setLogOverrides } from '@deltabox/toolchain/shared'; setLogConfig({ level: 'debug', color: false, name: 'dbx' }); // from dbx.config.* setLogOverrides({ level: 'warn' }); // from --log-level logger.info('human message'); logger.success('done'); logger.error('failed'); logger.raw(JSON.stringify(payload)); // machine output: no prefix, no colour ``` Use `raw` for machine output (`--json`): it is never prefixed or decorated. `logger.warn`/`error` write to stderr; `debug`/`info`/`success`/`raw` to stdout. Messages that start with whitespace (indented continuations) are never prefixed. This mirrors the [`logger` config section](/guide/config/logging) and the `--log-level ` / `--no-color` [CLI flags](/guide/cli#global-flags), which call `setLogConfig` / `setLogOverrides` under the hood. --- --- url: https://toolchain.deltabox.studio/guide/api/package-reference.md --- # Package reference ## `@deltabox/toolchain/shared` The base layer: the config model, logging, process/string/object utilities and the template engine. | Export | Description | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | | `defineConfig(config)` | Identity helper for a typed `dbx.config.*` (object, promise, or sync/async callback). | | `sectionEnabled(section)` / `sectionConfig(section)` | Opt-in-by-presence helpers for a config section. | | `resolveTokens(config)` | Flatten a config into `{{ token }}` replacements. | | `CONFIG_FILE_NAMES` | The config file names the loader probes, in priority order. | | `repoSlugFromUrl(url)` | Derive an `owner/name` slug from a repository URL. | | `findOwnCliEntry(cwd?)` / `localToolchainVersion(cwd?)` / `TOOLCHAIN_PACKAGE` | Locate a project's own toolchain bin + pinned version (prefer them over a global `dbx`). | | `createLogger(options)` / `logger` | The shared logger (see [Logging](/guide/api/logging)). | | `WARNING_MAP` / `ERROR_MAP` / `warningMessage(id, values?)` | Central messages keyed by stable logger/suppression ids. | | `spawnCross(cmd, args, options)` / `run(cmd, args, options)` | Cross-platform, shell-free spawning; `timeoutMs` kills the child tree. | | `killProcessTree(child)` | Kill a child process and its descendants (Windows `taskkill /t`). | | `readJsonFile` / `readJsonAt` / `toJson` / `writeJsonFile` | Small JSON file helpers. | | `mergeJsonAt(cwd, relative, patch)` | Deep-merge a patch over an existing JSON file. | | `deepMerge` / `applyOverrides` / `isPlainObject` / `sort` / `mapValues` | Object utilities (including layered typed overrides). | | `applyReplacements` / `applyReplacementsDeep` / `createReplacer` | The `{{ token }}` template engine (also `tokenPattern`, `extractTokens`). | | `DEFAULT_IGNORE_PATTERNS` / `DEFAULT_FMT_IGNORE_PATTERNS` / `DEFAULT_LINT_IGNORE_PATTERNS` | The shared build/cache ignores and the Oxfmt/Oxlint extensions. | | `resolveIgnorePatterns(cwd?, options?)` / `collectGitignorePatterns(cwd?)` | Defaults + `exclude` + `.gitignore` resolution, and the matching helpers (`isIgnoredPath`). | | `toYaml(value)` | A tiny YAML emitter for the GitHub Actions subset. | The config **types** and **schemas** also live here: `ToolchainConfig`, `DbxConfig`, `SyncTask`, `SyncContext`, `ManagedFile`, `ToolchainCommand`, `ToolchainOption`, `ToolchainProvider`, `ToolchainViteConfig` (Vite+'s config type), `Replacements`, and the generated `config-types` / `config-schema` exports. ```ts import { defineConfig, sectionEnabled, applyReplacements } from '@deltabox/toolchain/shared'; const text = applyReplacements('Hello {{name:world}}', { name: 'acme' }); ``` ## `@deltabox/toolchain/core` Loads `dbx.config.*`, builds the project graph and runs the sync tasks. | Export | Description | | ------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | `loadToolchainConfig(cwd?)` | Find + load the config, resolve tokens and the project type. | | `findConfigFile(cwd?)` | The config file path, if any. | | `runSync(options)` | Run the given `SyncTask[]` (or `only` a named subset) and return the summary. | | `runWatch(options)` | Sync once, then watch inputs and re-sync (honours `only`). | | `selectSyncTasks(tasks, only)` | Select tasks by name; throws on unknown names. | | `buildProjectGraph(root, options?)` | Discover workspace packages and their dependencies. | | `orderProjects(nodes)` / `detectPackageManager(root)` | Dependency ordering and package-manager detection. | | `writeProjectGraph` / `readProjectGraph` | Persist/read `.dbx/toolchain/projects.json`. | | `selectProjects(options)` | Select projects by kind/name/glob. | | `resetToolchain(options)` / `resetProjects(options)` | Remove generated files, the manifest and/or the config (`restore` hook runs first). | | `vscodeSyncTask(options?)` | The VS Code settings/extensions sync task. | | `resolveProjectType(cwd, config?)` / `detectProjectType(cwd)` | The project kind (`monorepo-root` / `monorepo-workspace` / `project`). | | `readManifest(cwd)` / `writeManifest(cwd, files)` | The `.dbx/toolchain/manifest.json` helpers. | | `resolveFmtCustomGroups(input?)` | Build the `sortImports` workspace group from every dbx project (graph-based). | ```ts import { defaultSyncTasks, runSync, selectSyncTasks } from '@deltabox/toolchain'; await runSync({ tasks: defaultSyncTasks(), only: ['tsconfig'], cwd, config, tokens, }); // Or select explicitly: const tasks = selectSyncTasks(defaultSyncTasks(), ['tsconfig']); ``` `only` implies `prune: false`, so a task subset never deletes files owned by the tasks it skipped. Unknown task names throw. ## `@deltabox/toolchain/config` The generic Zod/env engine (see the [Config Generator](/guide/config-generator/) guide for the full workflow). | Export | Description | | ---------------------------------------------------------------------------------------- | --------------------------------------------------------- | | `createConfigEngine(options)` | Bind scopes + prefix + root into a reusable engine. | | `configCommands({ engine, defaults? })` | The `config:*` commands for a consumer's own CLI. | | `formatGenerated(source, options?)` | Format generated output with Oxfmt (see below). | | `GENERATED_FORMAT_OPTIONS` | The baseline Oxfmt options used by the generators. | | `parseEnvTree(env, prefix?)` / `envString` / `envInt` / `envBool` / `envList` / `envMap` | The dependency-free runtime loader. | | `compileZodSchema(schema)` | Compile a Zod schema for fast repeated parsing. | | `envPathOf(env, prefix?)` / `metaOf(schema)` | Env-name to dotted-path derivation and `.meta()` reading. | | `findProjectRoot(start?)` / `monorepoRoot(from?)` | Walk up to the nearest `package.json`. | | `generateConfigTypes` / `generateConfigEnv` | Type/env generators (optional disk writes). | | `generateConfigMarkdown` / `generateConfigCli` | Markdown/CLI generators (optional disk writes). | | `generateCliDocs(commands, options?)` | A CLI reference from `ToolchainCommand` descriptors. | | `generateConfigJsonSchema` / `generateJsonSchemaFile` | JSON Schema generation. | | `normalizeJsonSchemaMetadata(node)` | Normalize Zod JSON Schema metadata for editor schemas. | | `generateEnv` / `generateEnvDts` / `generateConfigEnv` | `.env` / env type generation. | | `generateCliOptions` / `flagOf` / `cliFlagOf` / `createCliArgsParser` | CLI option generation + parsing. | ## `@deltabox/toolchain/vite` The Vite+ provider: resolving/running `vp`, the `dbx()` plugin and the `vite` sync task (see the [Providers](/guide/providers) guide). | Export | Description | | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | `resolveVp(cwd?)` / `hasVp(cwd?)` / `runVp(args, options?)` | Resolve and run the `vp` binary (local shim, global, then `npx`). | | `vpVersion(cwd?)` / `describeVp(cwd?)` / `which(bin)` | Version, resolution description and PATH lookup. | | `VP_COMMANDS` / `viteCommandName(name)` / `resolveVpCommand(name)` | The `vp` command catalog + name/alias resolution. | | `viteProvider()` / `viteCommands()` | The provider descriptor and the `dbx vite:*` commands. | | `dbx(options?)` / `DBX_TASKS` / `dbxTaskCommand(sub)` | The Vite/Vite+ plugin and the resolved `run.tasks` to spread in. | | `resolveDbx(cwd?)` / `runDbx(args, cwd?)` | Resolve/run the `dbx` binary (the project's own first; a pinned project never runs a global `dbx`). | | `resolveVite(options?)` | Resolve `toolchain.vite` (or a passed `toolchain`), with inline `vite` overrides. | | `detectViteSpecifier(cwd)` | Which package (`vite` / `vite-plus`) the project has, for the shim import. | | `renderViteConfig(specifier?)` / `viteConfigObject(config?)` | The managed `vite.config.ts` shim (per specifier) + the section minus `config`. | | `viteSyncTask()` | The `vite` sync task that writes the shim. | | `resolveFmtCustomGroups(input?)` | Re-exported from core: the `sortImports` workspace group resolver. | ## `@deltabox/toolchain/cli` | Export | Description | | ---------------------------------------------- | ---------------------------------------------------------------------- | | `registerToolchainCommands(program, commands)` | Wire framework-agnostic commands onto a commander program. | | `builtinCommands()` | The commands built into the root CLI (config commands need an engine). | | `createCli()` | Build the `dbx` commander program. | | `runCli(argv?)` | Parse and run the CLI (extracts logger flags, defaults bare Vite+). | | `withDefaultVite(argv, commandNames?)` | Insert `vite` before a bare Vite+ command. | | `extractLogOptions(argv)` | Pull `--log-level` / `--color` / `--no-color` out of argv. | See the [CLI](/guide/cli) guide for the routing behaviour. ## `@deltabox/toolchain/migrate` | Export | Description | | ----------------------------------------------------------------------------------- | ------------------------------------------------ | | `migrations()` / `listMigrations(cwd)` / `detectMigrations(cwd)` | The registered migrations and detection. | | `findMigration(idOrFrom)` / `runMigration(...)` / `runDetectedMigrations(ctx)` | Run one or every detected migration. | | `biomeToOx` / `eslintToOx` / `prettierToOx` / `dprintToOx` / `viteToToolchain` | The built-in `ToolchainMigration` objects. | | `mapBiomeToOxfmt` / `mapBiomeToOxlint` / `mapPrettierToOxfmt` / `mapDprintToOxfmt` | Source-to-Oxc mapping helpers. | | `extractViteObject(source)` / `scanLiteralObject(text)` / `foldViteIntoConfig(...)` | The `vite` source's extraction + splice helpers. | | `diffFiles(files)` / `hasPending(diffs)` | Diff planned files against disk (for `--check`). | | `mergeViteConfig(source, ...)` / `writeViteTarget(...)` | The Vite+ (`--target vite`) output helpers. | ## `@deltabox/toolchain/template` | Export | Description | | ------------------------------------------------------------------------------- | ---------------------------------------------------------- | | `loadTemplates(options)` / `collectSources(options)` | Resolve builtin/local/online sources into templates. | | `scaffoldTemplate(template, options)` | Copy a template into a target directory, resolving tokens. | | `addTemplateSource(cwd, source)` / `removeTemplateSource(cwd, key)` | Manage `.dbx/toolchain/templates.json`. | | `readTemplateRegistry` / `writeTemplateRegistry` | Read/write the template source registry. | | `BUILTIN_TEMPLATES` / `loadLocalTemplates(root, idPrefix?)` | The offline templates. | | `readTemplateDir` / `readTemplateManifest` / `writeTemplateManifest` | A local `template.json` directory. | | `parseSourceSpec(spec, cwd?)` / `gitTemplate(source)` / `loadNpmTemplates(pkg)` | Online source parsing/loading. | ## `@deltabox/toolchain/providers` | Export | Description | | --------------------------- | ----------------------------------------------------- | | `toolchainProviders()` | Every registered provider with its actions resolved. | | `providerActions(provider)` | The default actions for a provider (one per command). | | `viteProvider()` | The Vite+ provider descriptor. | ```ts import { toolchainProviders } from '@deltabox/toolchain/providers'; console.log(toolchainProviders().map(p => p.id)); ``` ## `@deltabox/toolchain/tsdown` | Export | Description | | -------------------------------------------------------------- | -------------------------------------------- | | `baseTsdownConfig` | The shared tsdown base config. | | `createTsdownConfig(overrides?)` / `createPostBanner(options)` | Build a tsdown config / a post-build banner. | | `cleanup(options?)` / `cleanupCommands()` | Remove build artifacts / the turbo cache. | ## `@deltabox/toolchain/tsconfig` | Export | Description | | ---------------------------------------------------------------------------- | ----------------------------------------------------- | | `tsconfigSyncTask()` | The tsconfig sync task. | | `tsconfigBaseTemplate` / `tsconfigDefaultTemplate` / `tsconfigBuildTemplate` | The base/default/build templates. | | `renderTsconfigBase` / `renderTsconfigDefault` / `renderTsconfigBuild` | Render the templates with replacements. | | `buildMonorepoTsconfig(options)` / `renderMonorepoTsconfig(options)` | The monorepo root tsconfig with references + aliases. | | `renderTsconfigTemplates(replacements?)` | Render every template at once. | | `resolveTsconfig(options?)` / `readTsconfig(file)` | Resolve paths/references/aliases (extends-aware). | | `generateTsconfigs(options)` / `runTsconfigCli(argv?)` | The tsconfig generator and its CLI. | | `tsconfigFileNames(config)` / `restoreTsconfigOverrides(options)` | Configured file names / remove applied overrides. | | `applyOverride` / `mergeOverride` / `revertOverride` | Override deep-merge + snapshot-aware revert helpers. | | `getSnapshot` / `setSnapshot` / `deleteSnapshot` | The manifest snapshot tree (repo-relative paths). | ## `@deltabox/toolchain/changelog` | Export | Description | | ---------------------------------------------------------------------- | ---------------------------------------------------- | | `runChangelog(argv?)` / `main` | Derive the bump and update the changelog + versions. | | `runWorkflows(options)` / `resolveWorkflowId(name)` | Trigger generated workflows with the vendor CLI. | | `workflowTargets(config)` / `workflowRunCommand(target, options)` | List enabled workflows / build a vendor CLI command. | | `renderWorkflow(id, options)` / `renderWorkflows(...)` | Render workflow YAML. | | `renderCommitlintConfig(options?)` / `changelogSyncTask()` | Commitlint config + the changelog sync task. | | `readCommits(range)` / `deriveBump(records)` / `buildSummary(records)` | Git commit reading, bump derivation and summaries. | | `renderChangelogEntry(...)` / `parseExistingChangelog(content)` | Changelog entry rendering/parsing. | | `formatBody(body)` / `formatLine(line, repo)` | Description + changelog-line normalization. | `formatLine(line, repo)` normalizes a changelog line in place: numeric-only short hashes (eg. `#4033009`) stay commit links, genuine issue numbers stay issue links, and a bare `BREAKING CHANGE:` marker is bolded unless it is already bold. ## `@deltabox/toolchain/biome` | Export | Description | | ----------------------------------------------------------------------- | ---------------------------------------------------------- | | `biomeConfig` / `biomeVscodeSettings` | The generic Biome config and recommended VS Code settings. | | `renderBiomeConfig(replacements?, overrides?)` / `renderBiomeJson(...)` | Resolve tokens + merge overrides, then serialize. | | `mergeBiomeConfig(base, patch)` / `renderVscodeSettings(indent?)` | Recursive merge and settings rendering. | | `biomeSyncTask()` | The biome sync task (writes `biome.json`). | ## `@deltabox/toolchain/project` | Export | Description | | -------------------------------------------------------------------------------------------- | -------------------------------------------------- | | `createRootProjectFiles(ctx)` / `createMonorepoRootFiles(ctx)` / `createWorkspaceFiles(ctx)` | Scaffold files for each project kind. | | `generateProjectConfigs(options)` | Generate `dbx.config.ts` for projects missing one. | | `renderToolchainConfig(ctx)` / `renderWorkspaceTsconfig(composite?)` | Render the tooling config / a workspace tsconfig. | | `renderGitignore()` | Render a generic `.gitignore`. | | `catalogsSyncTask()` / `renderYarnrc(catalog)` / `renderPnpmWorkspace(catalog, packages?)` | The catalogs sync task + catalog file renderers. | | `writeGeneratedFiles(files, cwd?)` | Write a `GeneratedFile[]`. | | `projectCommands()` | The `project:*` commands. | --- --- url: https://toolchain.deltabox.studio/guide/api/registering-commands.md --- # Registering commands Packages export framework-agnostic `ToolchainCommand[]` descriptors; wire them onto a commander program with `registerToolchainCommands` (this is how the bundled `dbx` binary, and a consumer's own CLI, are assembled): ```ts import { Command } from 'commander'; import { registerToolchainCommands } from '@deltabox/toolchain/cli'; import { configCommands } from '@deltabox/toolchain/config'; const program = new Command(); registerToolchainCommands(program, configCommands({ engine })); await program.parseAsync(); ``` Custom descriptors do not need to be added to `builtinCommands()` to appear in the generated reference. Pass the same command list to `generateCliDocs`: ```ts import { builtinCommands } from '@deltabox/toolchain/cli'; import { generateCliDocs } from '@deltabox/toolchain/config'; const markdown = generateCliDocs([...builtinCommands(), ...myCommands], { prefix: 'my-cli', }); ``` `generateCliDocs` omits `hidden` commands, keeps the first of any duplicate name, and renders args/options (with defaults) the same way as the `dbx` reference. --- --- url: https://toolchain.deltabox.studio/guide/api/subpath-exports.md --- # Subpath exports | Import | What it provides | | ------------------------------- | ----------------------------------------------------------------------------------------------- | | `@deltabox/toolchain/cli` | Build/register the `dbx` commander program + bare-command routing. | | `@deltabox/toolchain/core` | Load `dbx.config.*`, run/watch sync, the project graph, manifest, reset. | | `@deltabox/toolchain/config` | The Zod/env config engine, generators and the `config:*` commands. | | `@deltabox/toolchain/vite` | Resolve/run `vp`, the Vite+ provider, the `dbx()` plugin and `vite` sync. | | `@deltabox/toolchain/migrate` | Pluggable migrations to Oxfmt/Oxlint (Biome, ESLint, Prettier, Dprint). | | `@deltabox/toolchain/template` | The builtin/local/online template registry, loading and scaffolding. | | `@deltabox/toolchain/providers` | The provider registry (`toolchainProviders`) the editor reads. | | `@deltabox/toolchain/tsdown` | The tsdown base config, `createTsdownConfig`, post banner and cleanup. | | `@deltabox/toolchain/tsconfig` | The tsconfig sync task, templates and the tsconfig generator. | | `@deltabox/toolchain/changelog` | The changelog runner, workflows, commitlint and the changelog sync task. | | `@deltabox/toolchain/biome` | The generic Biome config, rendering helpers and the biome sync task. | | `@deltabox/toolchain/project` | Project/monorepo/workspace file generation and `dbx.config.ts` scaffolding. | | `@deltabox/toolchain/shared` | The config model, logger, provider model, template engine, ignore defaults, `run`/`spawnCross`. | --- --- url: https://toolchain.deltabox.studio/guide/api/umbrella.md --- # Umbrella (`@deltabox/toolchain`) The root re-exports `@deltabox/toolchain-core` and `@deltabox/toolchain-shared` **directly**, and exposes the remaining packages as **namespaces** plus the sync runner: ```ts import { // identity helper (typed config) defineConfig, // config + sync loadToolchainConfig, runSync, runWatch, defaultSyncTasks, // opt-in-by-presence helpers sectionEnabled, sectionConfig, resolveTokens, // logging logger, createLogger, // ignore-list defaults DEFAULT_IGNORE_PATTERNS, DEFAULT_FMT_IGNORE_PATTERNS, DEFAULT_LINT_IGNORE_PATTERNS, } from '@deltabox/toolchain'; // The other packages are namespaced: import { biome, changelog, config, core, project, shared, tsconfig, tsdown, } from '@deltabox/toolchain'; ``` ```ts const { config, tokens, projectType } = await loadToolchainConfig(process.cwd()); await runSync({ tasks: defaultSyncTasks(), cwd: process.cwd(), config, tokens, projectType }); ``` --- --- url: https://toolchain.deltabox.studio/guide/catalogs.md --- # Package catalogs A **catalog** is a shared `package -> version range` map. Workspaces declare a dependency as `catalog:` and the package manager resolves it from the catalog, so a version lives in one place instead of being repeated (and drifting) across every `package.json`. `toolchain.catalogs` writes that map into the file the package manager reads it from. Like every section it is **opt-in by presence** and root-only: a workspace never inherits it. ## Configuration ```ts [dbx.config.ts] export default defineConfig({ toolchain: { catalogs: { // The default catalog (`catalog:`). packages: { react: '^19.0.0', 'react-dom': '^19.0.0', lodash: '^4.17.21', }, // Named catalogs (`catalogs.`). named: { legacy: { react: '^18.0.0' }, }, }, }, }); ``` | Option | Type | Description | | ---------- | ---------------------------------------- | ---------------------------------------------- | | `packages` | `Record` | The default catalog: package -> version range. | | `named` | `Record>` | Named catalogs: name -> (package -> range). | | `enabled` | `boolean` | Master switch (default: enabled when defined). | A section with neither `packages` nor `named` writes nothing. ## Supported package managers | Manager | Catalog file | Notes | | ------- | --------------------- | --------------------------------------------------- | | `yarn` | `.yarnrc.yml` | `catalog:` / `catalogs:` (default + named). | | `pnpm` | `pnpm-workspace.yaml` | `catalog:` / `catalogs:`. | | `bun` | `package.json` | `workspaces.catalog`; declare it there. | | `npm` | — | No catalog support; dependencies are pinned inline. | The manager is read from the resolved `project.packageManager` (falling back to the root `package.json`'s `packageManager`, then `yarn`). ## Managed files `dbx sync` writes the catalog file only when it is: * **absent**, or * **already toolchain-managed** — it carries the `# Managed by @deltabox/toolchain (catalogs)` marker. A hand-written catalog file is never clobbered. The file is also marked as `keep`: removing the `catalogs` section (or replacing the file with your own) never deletes it, because the package manager reads it and it may hold hand-written keys. For Yarn the generated file also sets `nodeLinker: node-modules`; for pnpm it declares the `packages/*` globs on a monorepo root. Bun catalogs live in `package.json` `workspaces.catalog` and npm has no catalog support, so both warn instead of writing a separate file. ## Scaffolding `dbx project:init` and `dbx project:init:monorepo` write the catalog for the packages the scaffold depends on **and opt into it in the generated `dbx.config.*`**, so `dbx sync` owns and keeps the file in sync: ```ts [dbx.config.ts] toolchain: { project: { packageManager: 'yarn' }, catalogs: { packages: { '@deltabox/toolchain': '^2.5.0', typescript: '^6.0.3', // ... }, }, } ``` A single Bun project pins its dependencies (there is nowhere to declare a catalog); a Bun monorepo root writes the catalog into `package.json`. ## Keeping it in sync ```sh dbx sync # regenerate the catalog file dbx sync --only catalogs # just the catalog task dbx sync --check # CI: fail on drift ``` ## Programmatic API ```ts import { CATALOG_MARKER, CATALOG_PACKAGE_MANAGERS, catalogFileFor, catalogsSyncTask, hasCatalog, renderPnpmWorkspace, renderYarnrc, scaffoldCatalog, supportsCatalogs, } from '@deltabox/toolchain/project'; ``` | Export | Description | | ---------------------------- | -------------------------------------------------------------------- | | `catalogsSyncTask()` | The sync task that writes the managed catalog file. | | `catalogFileFor(pm)` | The file a manager reads its catalog from (`undefined` for npm/bun). | | `hasCatalog(root, pm, req?)` | Whether a default catalog exists (optionally covering `req`). | | `supportsCatalogs(pm)` | Whether the manager supports catalogs at all. | | `renderYarnrc(catalog)` | Render a `.yarnrc.yml` (node-modules linker + catalog(s)). | | `renderPnpmWorkspace(c)` | Render a `pnpm-workspace.yaml` (optional globs + catalog(s)). | | `scaffoldCatalog(pm, ex?)` | The catalog a scaffold writes (`undefined` when it writes none). | | `CATALOG_MARKER` | The marker line that identifies a toolchain-managed catalog file. | | `CATALOG_PACKAGE_MANAGERS` | The managers with catalog support (`yarn`, `pnpm`, `bun`). | --- --- url: https://toolchain.deltabox.studio/guide/changelog.md --- # Changelog ## CI The `ci` section drives everything Conventional Commits (by default): the CI that lints commit messages, the local husky hook, the `commitlint.config.mjs`, the GitHub workflows, and the changelog pipeline that bumps versions and writes `CHANGELOG.md`. Its `ci.changelog` config holds the changelog-specific options. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { ci: { ci: true, release: true, changelog: { branches: { main: 'main', dev: 'dev' }, commitlint: { types: ['feat', 'fix', 'docs', 'chore'], scopes: ['api', 'web', 'deps'], }, versioning: { target: 'individual', workspaces: ['packages/*', 'apps/*'] }, }, }, }, }); ``` `dbx sync` writes the opted-in GitHub workflows (`.github/workflows`), the commitlint config, and the husky `commit-msg` hook (`.husky/commit-msg`). Activate the hook with a `postinstall` script (Yarn Berry never runs `prepare`): ```json [package.json] { "scripts": { "commitlint": "commitlint", "postinstall": "husky" } } ``` See [Package scripts](/guide/scripts) for the full recommended script set. ## Group order and titles `groups` customizes the Conventional-Commits sections. It works on both `toolchain.changelog.groups` (manual runs) and `ci.changelog.groups` (CI + manual when inherited). An array sets the order and optionally the titles: ```ts ci: { changelog: { groups: [ { type: 'feat', title: 'New Features' }, { type: 'fix', title: 'Bug Fixes' }, 'perf', ], }, } ``` A record is a shorthand for the same thing, ordered by its keys. Use `false` to hide a group: ```ts groups: { feat: 'New Features', fix: 'Bug Fixes', chore: false, } ``` The default order is **Breaking Changes**, `feat`, `fix`, then the remaining types (`perf`, `refactor`, `docs`, ...). Types you do not mention are appended in the default order. `order` overrides the array/record position, and every title/order/hidden value accepts a callback. The whole `groups` value can also be a callback, so it can be computed from the types/items in the current run: ```ts groups: context => [ { type: 'feat', title: group => `New Features (${group.groupItems.length})`, order: 0, }, { type: 'fix', hidden: group => group.groupItems.length === 0 }, ...context.types.filter(type => !['feat', 'fix'].includes(type)), ]; ``` Callbacks are TS/JS-config-only. `dbx changelog --format-only` also uses the configured order/titles when regrouping existing entries. ## Breaking-change detection Breaking commits are detected by Conventional Commits rules by default: a `!` before the subject colon (`feat!:`, `feat(scope)!:`) or a `BREAKING CHANGE:` / `BREAKING-CHANGE:` footer line. Configure `breaking` on either `toolchain.changelog` or `ci.changelog` to override it: ```ts breaking: 'conventional', // default // breaking: 'any', // legacy loose match anywhere in the subject/body // breaking: false, // disable breaking detection // breaking: { pattern: '^feat!:' }, // breaking: { match: 'BREAKING CHANGE' }, // breaking: ({ subject, body }) => subject.includes('!'), ``` The same setting is used for version bumps, changelog grouping and `--format-only` regrouping. ## Manual `toolchain.changelog` The top-level `changelog` section configures manual runs for `dbx changelog`, the VS Code actions and the programmatic API. It accepts the pipeline options (commitlint, versioning, groups, VS Code settings), but not the CI-only workflow options or `branches`: ```ts [manual pipeline] toolchain: { changelog: { commitlint: { types: ['feat'], scopes: ['api'] }, versioning: true, vscode: true, }, } ``` Move the generated workflows to the top-level `ci` section, and put the pipeline options under `ci.changelog` when CI should use them too. Branches and workflow overrides only exist on `ci.changelog`: ```ts [after] toolchain: { ci: { ci: true, release: true, changelog: { branches: { main: 'main' }, commitlint: { types: ['feat'], scopes: ['api'] }, versioning: true, vscode: true, }, }, } ``` `ci.changelog` inherits `toolchain.changelog` when it is enabled without pipeline options (`ci.changelog: true`, a raw workflow string, or workflow-only overrides). Options set explicitly on `ci.changelog` win. ## Where to start * [CI](/guide/ci/) — the opt-in GitHub workflows (CI checks, changelog, release, issues, sync) and their common settings. * [Branches](/guide/changelog/branches) — `main`/`dev` branch globs. * [commitlint](/guide/changelog/commitlint) — config location, types/scopes and the no-commitlint fallback. * [Versioning](/guide/changelog/versioning) — which `package.json` versions a run bumps. * [CLI](/guide/changelog/cli) — running the changelog locally. * [Writing commit messages](/guide/changelog/writing-commit-messages) — avoiding shell-mangled messages. * [Troubleshooting](/guide/changelog/troubleshooting) — the upstream commitlint parser-preset error. * [Releases](/guide/ci/releases) — the aggregated release PR body. --- --- url: https://toolchain.deltabox.studio/guide/changelog/branches.md --- # Branches `ci.changelog.branches.main` and `ci.changelog.branches.dev` pick which branches the generated workflows target. Each accepts a glob or an array of globs: | Branch | Drives | Default | | ------ | ------------------------------------------------------------------------------ | -------------------- | | `main` | The **CI** workflow (push), the **release** workflow, and the release PR base. | `['main']` | | `dev` | The **changelog/version** workflow (push). | none — reuses `main` | `dev` has **no default**: when it is omitted the changelog workflow runs on `main` (so a single-branch repo needs no `dev`). Set it for a `dev` → `main` flow: ```ts ci: { changelog: { branches: { main: 'main', dev: 'dev' } } } // or multiple branches per purpose: ci: { changelog: { branches: { main: ['main', 'release/**'], dev: ['dev', 'next'] } } } ``` The `sync` workflow runs on both. Any per-workflow `pushBranches` override (eg. `ci.release.pushBranches`) still wins. --- --- url: https://toolchain.deltabox.studio/guide/changelog/cli.md --- # CLI ```sh dbx changelog # bump + write (uses ci.changelog.versioning) dbx changelog --dry-run # preview the root entry dbx changelog --range v1.2.0..HEAD # scan a specific range dbx changelog --version 2.0.0 # force a version (retags the changelogs) dbx changelog --format-only # regroup existing changelogs, no bump dbx changelog --release-notes 2.0.0 # print the release PR body for a version ``` The command reads `ci.changelog` and falls back to the manual `toolchain.changelog` pipeline options (commitlint, versioning, vscode). This lets a project keep manual/API changelog runs without enabling the CI workflow; `ci.changelog` inherits the same options when it does not override them. The generated `changelog` workflow runs `dbx changelog --range …` on every push to the `dev` branches (or `main` when `dev` is unset), so versioning is applied from CI as well as locally. `dbx workflow [id…]` triggers the generated workflows with their vendor CLI (`gh`, `glab` or `az`); with no ids it triggers every workflow you declared under `toolchain.ci`. See [Workflows](/guide/cli#workflows). ## Formatting `--format-only` regroups the existing entries without bumping versions. It keeps list items, unwraps hard-wrapped lines, keeps multi-line `BREAKING CHANGE:` footers together and preserves fenced code blocks. Numeric-only short hashes (eg. `#4033009`) are resolved against git, so they stay commit links while real issue numbers stay issue links. Existing subjects and descriptions are never re-rendered from git: hand corrections (including backtick fixes) survive, and a description is only filled in when the entry has none. See [Writing commit messages](/guide/changelog/writing-commit-messages). --- --- url: https://toolchain.deltabox.studio/guide/changelog/commitlint.md --- # commitlint By default the generated `commitlint.config.mjs` lives at the project root. Set `ci.changelog.commitlint.dir` to write it elsewhere: ```ts ci: { changelog: { commitlint: { dir: '.config'; } } } // -> .config/commitlint.config.mjs ``` When a custom dir is set, the generated husky hook and the CI commitlint step both pass `--config /commitlint.config.mjs`, so commitlint finds it. Note that editor tooling that discovers the config by convention (eg. the Conventional Commits VS Code extension, which loads from the repo root) expects the config at the root — keep the default unless you have a reason to move it. `commitlint.types` and `commitlint.scopes` are the allowed Conventional-Commits types/scopes: ```ts ci: { changelog: { commitlint: { types: ['feat', 'fix', 'docs', 'chore'], scopes: ['api', 'web', 'deps'], }, }, } ``` ## Disabling commitlint Set `ci.changelog.commitlint: false` (or `{ enabled: false }`) to generate changelogs **without** the Conventional-Commits rules. The changelog workflow is still generated, but dbx writes no `commitlint.config.mjs`, no husky hook and no commitlint step in the CI workflow. The changelog then falls back to a coarse grouping: commits are hoisted into `Major changes`, `Minor changes` and `Patch changes` buckets (breaking commits and `feat:` go to major/minor; everything else is a patch), while the package changes section (eg. new packages) is kept. This is intentionally less detailed than the rule/scope grouping, but it works with any commit-message style. > The extension's `No "exports" main defined in … conventional-changelog-conventionalcommits …` > error is an upstream commitlint bug (ESM-only parser preset), not a config > discovery problem. See [Troubleshooting](/guide/changelog/troubleshooting). --- --- url: https://toolchain.deltabox.studio/guide/changelog/troubleshooting.md --- # Troubleshooting ## `No "exports" main defined in … conventional-changelog-conventionalcommits` Seen from the Conventional Commits VS Code extension (`vivaxy.vscode-conventional-commits`), usually alongside empty type/scope suggestions and a `The cwd is …` line: ``` commitlint: Error: No "exports" main defined in node_modules/conventional-changelog-conventionalcommits/package.json ``` This is a **known upstream commitlint bug** (commitlint#4864): the generated config `extends: ['@commitlint/config-conventional']`, which sets `parserPreset: 'conventional-changelog-conventionalcommits'`, and v10 of that package is **ESM-only** (`exports` has no `require`/`default`), so the extension's bundled (older) commitlint cannot `require.resolve` it. The config is read fine — the *preset* fails to resolve, so the extension falls back to empty rules. The husky hook and CI are unaffected when commitlint is recent enough to resolve pure-ESM presets (commitlint ≥ 21.2.3). If your local commitlint is older, pin the preset to the last CJS-compatible release: ```json [package.json] { "resolutions": { "conventional-changelog-conventionalcommits": "9.3.1" } } ``` (Use `overrides` instead of `resolutions` for npm.) Updating the extension to a version bundling the fixed commitlint also resolves it. --- --- url: https://toolchain.deltabox.studio/guide/changelog/versioning.md --- # Versioning `ci.changelog.versioning` controls whether — and where — a changelog run writes `version` fields into `package.json`. It accepts a boolean or an object, and defaults to `true`. ```ts ci: { changelog: { versioning: true } } // uniform, defaults ci: { changelog: { versioning: false } } // never write versions ci: { changelog: { versioning: { target: 'root-only' } } } ci: { changelog: { versioning: { target: 'individual', workspaces: ['packages/*'] } } } ``` ## `target` | Target | What it bumps | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `uniform` | The root (when it has a `package.json`) **and every workspace** to one shared version. **Default.** | | `root-only` | Only the root `package.json`. Workspaces are left untouched and get no changelog entry. | | `individual` | The root tracks the overall bump; **each workspace is bumped on its own** from the commits touching its files, so versions can drift apart (like an "independent" monorepo). | With `versioning: false` no `version` field is written at all — the changelog entries are still generated (headed as `uniform` would), so you can keep versioning in another tool (for example changesets). ## `workspaces` An array of glob patterns selecting the workspaces to version, relative to the repo root. It resolves in this order: 1. the `workspaces` patterns you pass, when you pass them; 2. otherwise the root `package.json` `workspaces` field, when present; 3. otherwise the root `package.json` alone (a single-package repo); 4. otherwise a warning is logged and nothing is versioned. ```ts ci: { changelog: { versioning: { target: 'individual', workspaces: ['packages/*', '!packages/private-*'], }, }, } ``` `individual` uses a `git log` path filter per workspace, so a workspace is only bumped when the scanned commit range touched a file inside it — or a file in one of the workspaces it depends on (see [Workspace changelogs](#workspace-changelogs)). The root's version always follows the overall bump derived from every scanned commit. ## How a run decides its versions Each run: 1. reads the commits in the range (`--range`/`--processing-method`, default `HEAD`); 2. derives the semver bump (`feat` → minor, `fix`/other → patch, `!` or a `BREAKING CHANGE` footer → major); 3. plans the versions per `target` + `workspaces` (see above); 4. writes the plan's `package.json` versions and prepends the new entry to the root `CHANGELOG.md` and each versioned workspace's `CHANGELOG.md`. Use `dbx changelog --dry-run` to preview the proposed root entry without touching any file; the log line reports the active `target` (or `disabled`). ## Workspace changelogs Each versioned workspace gets **its own `CHANGELOG.md`** (created if missing), headed by the version the plan assigned it. A workspace's entry only lists its **related commits**: those touching the workspace directory **or any workspace it transitively depends on**, resolved from the project dependency graph (`.dbx/toolchain/projects.json`, written by `dbx projects`). So a change in a shared package shows up in the changelog of every package that consumes it, not just in its own. Without a graph (eg. `dbx projects` was never run), a workspace falls back to filtering on its own directory only. A workspace with no related commits is skipped (no empty entry, no file created). ## Custom bump rules `versioning.bump` maps `major`, `minor` and `patch` to a matcher. Matchers can be booleans, substrings, `/regex/` strings, arrays, objects (`{ type, types, scope, scopes, pattern, match, breaking }`) or callbacks. ```ts ci: { changelog: { versioning: { bump: { major: { scope: 'api', breaking: true }, minor: [{ type: 'feat' }, /^feat\(ui\)/], patch: 'fix', }, // Apply one bump per matching change instead of once per run. allowOverBump: false, }, }, } ``` Callbacks receive `{ env, logger, packageJson?, commit }` and may return a boolean or a version index. ## Commit credits and formatting `versioning.commit` controls the commit bullet credits. All options default to `true` except `authorAvatar`. ```ts ci: { changelog: { versioning: { commit: { author: true, authorAvatar: true, commitSha: true, issues: true, format: ({ commit, author, issues, repo }) => `${commit.subject} — ${author.name}${issues.length ? ` (${issues.length} issues)` : ''}`, }, }, }, } ``` ## Providers `versioning.provider` selects the changelog provider: * `default` (or omitted) uses the built-in pipeline. * `changesets` runs `npx changeset version` and leaves changelog/version output to changesets. * a callback `(ctx) => string | void` can replace the summary. * an object `{ id, run(ctx) }` can run custom logic and optionally return the summary string. --- --- url: https://toolchain.deltabox.studio/guide/changelog/writing-commit-messages.md --- # Writing commit messages The changelog is rendered from commit **subjects and bodies**, so what you commit is what readers see. Watch out for characters that a **shell** re-interprets — especially when a message is drafted by an **AI/LLM** and committed through `git commit -m "…"`. ## Shell escaping * **PowerShell** treats a backtick as its escape character, so `` `r `` / `` `n `` / `` `t `` / `` `b `` become CR / LF / TAB / backspace. A preceding `\` is left behind, so `` `resolveVite `` can land in the changelog as a stray `\`, a CR and `esolveVite`. * **bash** runs an unquoted `` `…` `` as command substitution, and `$(…)` / `${…}` expand before `git` ever sees them. The result is invisible control characters, stray `\` and dropped letters. `sanitizeText` maps the common control characters back to the letter they replaced (`BEL`→`a`, `BS`→`b`, `FF`→`f`, `VT`→`v`, `TAB`→`t`, `LF`→`n`, `CR`→`r`, `ESC`→`e`, `NUL`→`0`) and drops the stray `\`, so words like `any`, `false` and `bin` are restored. It also repairs the common backtick-to-backslash artifacts (a paired `\yarn cache dir\` or a lone trailing `baseTsdownConfig\`) back to inline code, while leaving Windows paths and regexes alone. A character the shell consumed without leaving a trace still cannot be recovered. ## Authoring safely * Prefer a **file over `-m`**: `git commit -F message.txt`, or a quoted heredoc (`git commit -F - <<'EOF' … EOF`) — nothing is re-interpreted. Avoid assembling multi-line messages inline in PowerShell. * When an AI/LLM drafts the message, strip **typographic characters** before committing: curly quotes (`’ “ ”`), em/en dashes (`— –`), non-breaking spaces, zero-width characters and emoji all survive into the changelog and can break `commitlint` or the markdown. * **Preview the entry**: `dbx changelog --dry-run` prints the proposed entry — check it for stray `\`, `\r`, `\b`, `\t` or mojibake before releasing. --- --- url: https://toolchain.deltabox.studio/guide/ci.md --- # CI `dbx sync` writes the opted-in workflows for **GitHub Actions**, **GitLab CI** and **Azure Pipelines**. Every workflow is **opt-in**: declare it under `toolchain.ci` to write it; omit it (or set it to `false`) and the toolchain leaves that file unmanaged. Every id (`ci`, `changelog`, `release`, `issues`, `sync`) accepts one of four forms: * `true` / `false` — write the generated GitHub default, or skip it; * a **`WorkflowOptions` object** — override parts of the GitHub default (branches, triggers, extra top-level keys, extra steps/jobs, token overrides); * a **complete vendor workflow** — `{ provider, workflow }` for `github`, `gitlab` or `azure`, written as-is; * a **raw YAML string** — written **verbatim** as the workflow file, for a workflow the object model can't express. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { ci: { ci: true, // the generated GitHub CI checks issues: false, // don't write issues.yml release: { pushBranches: ['main'] }, // override the GitHub default changelog: { branches: { main: 'main', dev: 'dev' } }, // A complete GitLab pipeline, written to `.gitlab-ci.yml`: sync: { provider: 'gitlab', workflow: { stages: ['check'], check: { stage: 'check', script: ['yarn dbx sync --check'] }, }, }, }, }, }); ``` A raw string is **not** merged with the default, is **not** token-replaced, and gets **no generated header** — it is the file's exact content. The `ci.changelog.branches` defaults don't apply to it either (it defines its own triggers). An empty string disables the workflow. The `ci` key doubles as the CI-checks workflow (`ci.yml`) and the section name, so `toolchain.ci.ci` is the checks workflow. Set `toolchain.ci.enabled` to `false` to disable the whole section (and every nested workflow) at once. ## Vendor workflows A complete vendor workflow always has `provider` and `workflow`; `filename`, `dir` and `enabled` are optional: | `provider` | Default file | Run with | | ---------- | --------------------- | ------------------ | | `github` | `.github/workflows/` | `gh workflow run` | | `gitlab` | `.gitlab-ci.yml` | `glab ci run` | | `azure` | `azure-pipelines.yml` | `az pipelines run` | ```ts [dbx.config.ts] toolchain: { ci: { // .gitlab-ci.yml (the default GitLab path) ci: { provider: 'gitlab', workflow: { stages: ['test'], test: { stage: 'test', script: ['yarn test'] } }, }, // azure-pipelines.yml (the default Azure path) release: { provider: 'azure', workflow: { trigger: ['main'], pool: { vmImage: 'ubuntu-latest' }, steps: [] }, }, // A complete GitHub workflow, written to the default `.github/workflows/deploy.yml` custom: { deploy: { provider: 'github', workflow: { name: 'Deploy', on: { push: { branches: ['main'] } }, jobs: { deploy: { 'runs-on': 'ubuntu-latest', steps: [] } }, }, }, }, }, } ``` `filename` and `dir` override the generated path for any provider. The workflow object is written as-is (only the toolchain's `{{ token }}` replacements are applied), so it must be a complete pipeline. `dbx workflow` dispatches each workflow with its vendor CLI — see [CLI](/guide/cli#tsconfig-cleanup-changelog-workflows). ## Typed GitHub workflows If you prefer a typed builder over hand-writing YAML, use [`@github-actions-workflow-ts/lib`](https://github.com/emmanuelnk/github-actions-workflow-ts) (and its `@github-actions-workflow-ts/actions` catalog). The builder produces a plain workflow object in `.workflow`, which the `github` vendor form accepts: ```ts [dbx.config.ts] import { ActionsCheckoutV4, ActionsSetupNodeV4 } from '@github-actions-workflow-ts/actions'; import { NormalJob, Step, Workflow } from '@github-actions-workflow-ts/lib'; import { defineConfig, type GitHubWorkflow } from '@deltabox/toolchain'; const test = new NormalJob('test', { 'runs-on': 'ubuntu-latest' }); test.addStep(new ActionsCheckoutV4({ name: 'Checkout' })); test.addStep(new ActionsSetupNodeV4({ name: 'Setup Node', with: { 'node-version': '22' } })); test.addStep(new Step({ name: 'Test', run: 'yarn test' })); const ci = new Workflow('ci.yml', { name: 'CI', on: { push: { branches: ['main'] } }, }); ci.addJob(test); export default defineConfig({ toolchain: { ci: { ci: { provider: 'github', workflow: ci.workflow as unknown as GitHubWorkflow, }, }, }, }); ``` The toolchain does not depend on the builder, so the `workflow` field is typed from its own vendored GitHub schema. The two schemas can drift slightly (for example the builder's `Partial` makes `on` optional, and its `permissions` allows `id-token: read`), so cast the object when TypeScript rejects it. At runtime the workflow is validated by GitHub, exactly like a hand-written object. ## Workflows | Workflow | GitHub file | Page | | --------- | --------------- | ----------------------------------------- | | CI checks | `ci.yml` | [CI checks](/guide/ci/ci) | | Changelog | `changelog.yml` | [Changelog workflow](/guide/ci/changelog) | | Release | `release.yml` | [Releases](/guide/ci/releases) | | Issues | `issues.yml` | [Issues](/guide/ci/issues) | | Toolchain | `sync.yml` | [Sync](/guide/ci/sync) | Extra workflows can be defined under `toolchain.ci.custom.` (for example `ci.custom.deploy` writes `.github/workflows/deploy.yml`, or the vendor default when it sets `provider`). Each accepts the same workflow forms, plus `filename` and `dir` overrides. See [Workflow settings](/guide/ci/settings) for the shared object options and the [Changelog](/guide/changelog/) guide for the versioning/commitlint pipeline. --- --- url: https://toolchain.deltabox.studio/guide/ci/changelog.md --- # Changelog workflow The `ci.changelog` workflow (`changelog.yml`) generates the changelog and applies the version bump. It runs on every push to `ci.changelog.branches.dev` (or `main` when `dev` is not set), then commits the updated `CHANGELOG.md` files and `package.json` versions back to the branch. ```ts [dbx.config.ts] toolchain: { ci: { changelog: { branches: { main: 'main', dev: 'dev' }, pushBranches: ['dev'], // optional per-workflow override }, }, } ``` `ci.changelog` is also the changelog **pipeline** config. Its options are documented in the [Changelog](/guide/changelog/) guide: * [Branches](/guide/changelog/branches) * [commitlint](/guide/changelog/commitlint) * [Versioning](/guide/changelog/versioning) * [CLI](/guide/changelog/cli) The workflow is opt-in (`ci.changelog: true` or an object). `ci.changelog: false` leaves it unmanaged; `ci.changelog.commitlint: false` still generates the workflow but skips commitlint, its config and the local hook. `ci.changelog` inherits the manual `toolchain.changelog` pipeline options when it does not set them itself (for example `ci.changelog: true` or an object that only sets workflow options like `pushBranches`). Explicit `ci.changelog` options win; `branches` only exists on `ci.changelog`. The workflow itself can target another vendor with `{ provider, workflow }` (GitLab/Azure); the versioning/commitlint pipeline still lives in `toolchain.changelog` (or the `ci.changelog` object options), not in the vendor workflow. See [Workflow settings](/guide/ci/settings#vendor-workflows). --- --- url: https://toolchain.deltabox.studio/guide/ci/ci.md --- # CI checks The `ci.ci` workflow (`ci.yml`) is the repository's main check. It runs on pushes to `ci.changelog.branches.main` and on pull requests to the generated branch globs, and its default job: 1. checks out the repo, sets up Node and Corepack, and installs dependencies; 2. runs commitlint over the pushed range — **removed when commitlint is disabled** with `ci.changelog.commitlint: false`; 3. runs `dbx sync --check` so generated files cannot drift; 4. runs the package manager's `build`. ```ts [dbx.config.ts] toolchain: { ci: { ci: { pullRequestBranches: ['main', 'dev', 'release/**', 'dependabot/**'], steps: { after: [ { name: 'Format + lint', run: 'yarn check:fix' }, { name: 'Test', run: 'yarn test' }, ], }, }, }, } ``` Use [Workflow settings](/guide/ci/settings) for the shared options. The workflow is opt-in: `ci: { ci: true }` writes it; omitting `ci.ci` (or setting it to `false`) leaves the file unmanaged. Set `provider` (`gitlab`/`azure`) with a complete `workflow` object to write that vendor's pipeline instead. --- --- url: https://toolchain.deltabox.studio/guide/ci/issues.md --- # Issues The `ci.issues` workflow (`issues.yml`) normalizes new issue titles from the issue's labels: a bug-labelled issue gets a `[bug]` prefix, an enhancement/feature/feat-labelled issue gets `[feat]`; issues without either label are left untouched. ```ts [dbx.config.ts] toolchain: { ci: { issues: true, }, } ``` The workflow is opt-in and only runs on `issues: opened`. It needs the `issues: write` permission, which the generated default declares. Use [Workflow settings](/guide/ci/settings) to change its triggers or add jobs, or set `provider` with a complete GitLab/Azure `workflow` to target another vendor. --- --- url: https://toolchain.deltabox.studio/guide/ci/releases.md --- # Releases The generated `ci.release` workflow (`release.yml`) opens a release PR whose body is the **aggregated changelog**: every entry from the last released version up to the latest one, not just the newest entry. So a release that skips versions (eg. `v1.7.0` → `v1.7.8`) still documents `1.7.1`–`1.7.8` in one place. The body is produced by `dbx changelog --release-notes `, which only considers tags whose version actually appears in `CHANGELOG.md` (so unrelated CI/calendar tags are ignored). For a first release, when there is no prior version, it falls back to the newest changelog entry instead of the whole file. ```ts [dbx.config.ts] toolchain: { ci: { release: { pushBranches: ['main'] }, }, } ``` The workflow is opt-in. Use [Workflow settings](/guide/ci/settings) to override the job's extra steps/jobs or supply a fully hand-authored workflow. It can also be a complete GitLab/Azure pipeline via `{ provider, workflow }`. --- --- url: https://toolchain.deltabox.studio/guide/ci/settings.md --- # Workflow settings A workflow can be a boolean, a raw YAML string, a complete vendor workflow (`{ provider, workflow }`), or a `WorkflowOptions` object. The object is merged over the generated GitHub default, so you only describe what changes. These options apply to every GitHub workflow (`ci`, `changelog`, `release`, `issues`, `sync`). ```ts [dbx.config.ts] toolchain: { ci: { ci: { enabled: true, pushBranches: ['main', 'next'], pullRequestBranches: ['main', 'release/**'], triggers: { schedule: [{ cron: '0 6 * * *' }] }, tokens: { nodeVersion: '24' }, extra: { permissions: { contents: 'read' } }, steps: { after: [{ name: 'Lint', run: 'yarn lint' }], }, jobs: { smoke: { 'runs-on': 'ubuntu-latest', steps: [{ run: 'echo ok' }], }, }, }, }, } ``` | Option | What it does | | --------------------- | ------------------------------------------------------------------------------- | | `enabled` | Enable/disable this workflow (default: enabled when present). | | `provider` | Vendor for a complete `workflow` object (default `github`). | | `pushBranches` | Replace `on.push.branches`. | | `pullRequestBranches` | Replace `on.pull_request.branches`. | | `triggers` | Merge extra `on.*` triggers. | | `extra` | Merge extra top-level keys (`permissions`, `concurrency`, `env`, ...). | | `steps` | Inject steps before/after a job's generated steps (one group or per-job). | | `jobs` | Merge extra complete jobs. | | `tokens` | Override workflow tokens (`nodeVersion`, `packageManager`, `yarnVersion`, ...). | | `filename` | Override the generated file name (default `ci.yml`, `release.yml`, ...). | | `dir` | Override the output directory (default `.github/workflows`). | ## Steps `steps` accepts one group or an array of groups. A group targets a job id (default: the first/primary job) and can add steps before or after the generated ones: ```ts steps: [ { job: 'ci', before: [{ name: 'Install extras', run: 'yarn add -D foo' }] }, { job: 'ci', after: [{ name: 'Smoke', run: 'yarn smoke' }] }, ]; ``` ## Tokens The default workflow templates use `{{ token }}` replacements. `tokens` merges over the project-wide values, so one workflow can use a different Node version or package manager without changing `project`. Overriding `packageManager` switches to that manager's default version unless you also set `packageManagerVersion` (or the legacy `yarnVersion` alias). ## Raw YAML A string is used as the workflow file's exact content. It is not merged, not token-replaced and gets no generated header; an empty string disables it. ## Vendor workflows Set `provider` (`github`, `gitlab` or `azure`) with a complete `workflow` object to write another CI vendor's pipeline instead of the generated GitHub default. The object is written as-is (with the toolchain's `{{ token }}` replacements applied), so it must be a complete pipeline: ```ts [dbx.config.ts] toolchain: { ci: { ci: { provider: 'gitlab', workflow: { stages: ['test'], test: { stage: 'test', script: ['yarn test'] }, }, }, release: { provider: 'azure', workflow: { trigger: ['main'], pool: { vmImage: 'ubuntu-latest' }, steps: [{ script: 'yarn build' }], }, }, }, } ``` The default paths are `.gitlab-ci.yml` and `azure-pipelines.yml` (GitHub keeps `.github/workflows/.yml`); `filename` and `dir` override them. A complete vendor workflow defines its own triggers, so the `ci.changelog.branches` defaults do not rewrite them. `dbx workflow` runs each workflow with its vendor CLI (`glab ci run`, `az pipelines run --name `, or `gh workflow run`). Set `setup: true` to inject the provider-specific setup before the pipeline's own steps: GitHub gets the checkout/Node/Corepack/install steps, GitLab gets a `node:` image plus a `before_script`, and Azure gets `UseNode@1` plus an install script. It defaults to `false`, so the workflow is written as-is. If a hand-written `.gitlab-ci.yml` / `azure-pipelines.yml` already exists, sync leaves it untouched and records a workflow conflict, exactly like a GitHub workflow. Set `ci.: { enabled: false }` to opt out explicitly. ## Custom workflows Add any number of extra workflows with `toolchain.ci.custom`, keyed by workflow id. A custom workflow is enabled by **presence** (there is no boolean form): a string is written verbatim, a complete vendor workflow (`{ provider, workflow }`) is written as-is, or an object is merged over an empty GitHub workflow — build it with `triggers`, `extra` and `jobs` (plus `filename` / `dir`): ```ts [dbx.config.ts] toolchain: { ci: { custom: { deploy: { triggers: { workflow_dispatch: {}, push: { branches: ['main'] } }, filename: 'deploy.yml', // default: deploy.yml dir: '.github/workflows', // default jobs: { deploy: { 'runs-on': 'ubuntu-latest', steps: [ { uses: 'actions/checkout@v4' }, { run: 'yarn deploy' }, ], }, }, }, }, }, } ``` This writes `.github/workflows/deploy.yml` and manages it like any built-in workflow (the generated header is prepended, and `dbx sync --check` tracks it). Set `setup: true` to inject the shared checkout/Node/Corepack/install steps into each job; steps the job already defines are not duplicated. ## Existing workflows If a generated workflow path already contains a hand-written workflow that dbx has never managed (no manifest entry and no generated header), `dbx sync` leaves it untouched, warns, and records the conflict in `.dbx/toolchain/manifest.json`. Set `ci.: { enabled: false }` (or, for a custom workflow, `ci.custom.: { enabled: false }`) to make the opt-out explicit. The file is also never deleted when the workflow is later disabled, because dbx did not create it. Use `dbx workflow --list` (or `--list --json`) to see the enabled workflows and their exact paths; `dbx workflow ` honours `filename` and `dir` overrides. --- --- url: https://toolchain.deltabox.studio/guide/ci/sync.md --- # Sync The `ci.sync` workflow (`sync.yml`) verifies that every generated file is up to date: it runs `dbx sync --check` on pushes (to both the `main` and `dev` branch globs) and on pull requests. A drift in a workflow, tsconfig, `vite.config.ts` or any other managed file fails the job. ```ts [dbx.config.ts] toolchain: { ci: { sync: true, }, } ``` The workflow is opt-in. The `ci` check workflow also runs `dbx sync --check`, so `sync.yml` is useful when you want the check to run on pull requests without the full build. See [Workflow settings](/guide/ci/settings) for trigger overrides, or set `provider` with a complete GitLab/Azure `workflow` to target another vendor. --- --- url: https://toolchain.deltabox.studio/guide/cli.md --- # CLI The `dbx` binary is composed from framework-agnostic command descriptors. Commands that run against a project load its `dbx.config.ts` first, so config-derived defaults apply unless you override them on the command line. ## Global flags | Flag | Description | | ------------------- | -------------------------------------------------------------- | | `--log-level ` | Minimum log level: `debug`, `info`, `warn`, `error`, `silent`. | | `--color` | Force coloured output (the default). | | `--no-color` | Disable coloured output. | | `--no-update-check` | Do not check for a newer `dbx` version. | | `--config ` | Use a specific config file instead of discovering one. | | `--no-config` | Ignore config files and use CLI flags/env defaults only. | These override the [`logger` config](/guide/config/logging) and may appear before or after the subcommand. `--config` resolves relative to the current directory (absolute paths are used as-is); `--no-config` skips discovery entirely (including inherited parent configs). `dbx` checks the npm registry in the background (about once a day) and prints a short notice with a link to the release when a newer version is published. The check is skipped in CI and when `NO_UPDATE_NOTIFIER` or `DBX_NO_UPDATE_CHECK` is set, and can be turned off with `--no-update-check`. ## Interactive CLI The CLI is scriptable first: every prompt has a flag equivalent. In a TTY, however, some commands prompt when the flags are omitted: ```sh dbx create # template + name + scope + template inputs/actions dbx migrate --recursive # choose the projects to migrate ``` `dbx migrate` detects migrations automatically without `--from`; the prompt is only the project picker for `--recursive`. `--json`, a piped stdin, or the explicit flags disable prompts. See [Scaffolding](#scaffolding) and [Migrations](#migrations) for the flag forms. ## Output Colour is on by default and follows `--no-color` / the [`logger` config](/guide/config/logging). ## Core | Command | Description | | ----------- | -------------------------------------------------------------------------------------------------------------------- | | `projects` | Discover projects and write `.dbx/toolchain/projects.json`. | | `sync` | Regenerate every managed file (`--check`, `--recursive`, `--watch`, `--force`, `--only`, `--json`). | | `watch` | Sync once, then watch key inputs and re-sync. | | `reset` | Remove generated files, the manifest and/or the config (`--all`, `--recursive`); reverts `tsconfig.overrides` first. | | `providers` | List registered providers + their commands (`--json`). | ### Task-only sync `dbx sync --only ` runs just the named sync tasks (comma-separated) and does **not** prune files owned by the tasks it skipped. This is useful at the end of a build, when only the tsconfig aliases/references need refreshing: ```sh dbx sync --only tsconfig ``` Unknown task names fail with the list of available tasks. ## Scaffolding | Command | Description | | ------------------------ | ---------------------------------------------------------------------------------------------------------------- | | `project:init` | A single-package TypeScript project (`--package-manager`). | | `project:init:monorepo` | A monorepo root (add `--example` for one example workspace). | | `project:init:workspace` | A workspace package into an existing monorepo root. | | `project:config` | Generate `dbx.config.*` for projects missing one (TS/JS, `--skip-config` for graph only; folds `vite.config.*`). | | `create` | Scaffold from a template (`--template`/`--kind`, `--name`, `--scope`, `--out`). | Templates that declare inputs/actions are prompted for them (`--var key=value`, `--action `), see [Templates](/guide/templates). On an interactive terminal, a bare `dbx create` (no `--template`/`--kind`/`--name`) prompts for the template, name and scope with clack; Ctrl-C cancels. See [Templates](/guide/templates). ## Templates | Command | Description | | ------------------- | ------------------------------------------------------------------------------------ | | `template:list` | List templates (`--kind`, `--registry`, `--online`, `--json`). | | `template:init` | Write a `template.json` to a directory (`--kind`, `--name`, `--add`, `--no-global`). | | `template:add` | Record a source (auto-inits local; `--no-global` keeps it project-only). | | `template:remove` | Remove a source by id or `type:locate`. | | `template:registry` | List/add/remove the global template search paths (`--vscode-dir`, `--clear`). | | `registry` | List/add/remove/set template **or provider** registries (`--kind`, `--scope`). | | `template:info` | Show one template. | See [Templates](/guide/templates). ## Migrations ```sh dbx migrate --from biome --write # .oxfmtrc.json + .oxlintrc.json dbx migrate --from biome --from prettier --write # several sources dbx migrate --from biome --target vite --write dbx migrate --from oxc --write # .oxfmtrc/.oxlintrc -> dbx.config dbx migrate --from all # every detected source dbx migrate --from vite --recursive --write # migrate every detected project dbx migrate --recursive # interactive project picker dbx migrate --check # CI: exit 1 when stale dbx migrate:list ``` See [Migrations](/guide/migrations/). ## Tsconfig / cleanup / changelog / workflows | Command | Description | | --------------------------- | ---------------------------------------------------------------------------------- | | `tsconfig` | Regenerate workspace `tsconfig.json` files (`--restore [kind]` reverts overrides). | | `cleanup` / `cleanup:turbo` | Remove build artifacts / the turbo cache. | | `changelog` | Derive the semver bump and update CHANGELOG + versions. | | `workflow` | Trigger generated workflows with their vendor CLI (`gh`, `glab` or `az`). | ### Workflows `dbx workflow [id…]` runs every enabled workflow (or the named ids/custom ids/file names) with the CLI for its provider: ```sh dbx workflow # every enabled workflow dbx workflow ci release # by id dbx workflow deploy.yml # by file name dbx workflow --list --json # ids, paths and providers dbx workflow ci --ref main # pass a branch/ref dbx workflow ci --gh /path/to/gh --glab glab --az az ``` GitHub uses `gh workflow run `, GitLab `glab ci run` (the project's configured CI file), and Azure `az pipelines run --name ` (the file stem by default). When the matching CLI is not installed the command reports it and continues with the other workflows. See [CI](/guide/ci/) for the vendor workflow config. ## Providers Every provider subcommand is a passthrough. Vite+ commands are reachable both directly and through the `vite` namespace: ```sh dbx fmt # the active formatter (vp fmt when Oxc) dbx run -r build # forwards to `vp run -r build` dbx vite dev # explicit namespace (always works) dbx vite:build # named alias for `vp build` ``` A bare `dbx ` is forwarded to Vite+ when the name (or a Vite+ alias) matches — built-in commands win, so `dbx migrate` stays the toolchain's migration. The `vite` command forwards its operands verbatim, so **every** Vite+ CLI command and flag is reachable. ## Programmatic The CLI is assembled from framework-agnostic command descriptors, so a consumer can build its own program (or reuse the routing helpers): ```ts import { createCli, runCli, builtinCommands, registerToolchainCommands, withDefaultVite, extractConfigSelection, extractLogOptions, } from '@deltabox/toolchain/cli'; ``` | Export | Description | | ---------------------------------------------- | ---------------------------------------------------------------------- | | `createCli()` | Build the `dbx` commander program (global flags + built-in commands). | | `runCli(argv?)` | Parse and run the CLI (extracts logger flags, then defaults Vite+). | | `builtinCommands()` | The commands built into the root CLI (config commands need an engine). | | `registerToolchainCommands(program, commands)` | Wire `ToolchainCommand[]` descriptors onto a commander program. | | `withDefaultVite(argv, commandNames?)` | Insert `vite` before a bare Vite+ command (built-ins win). | | `extractLogOptions(argv)` | Pull `--log-level` / `--color` / `--no-color` out of argv. | | `extractConfigSelection(argv)` | Pull `--config ` / `--no-config` out of argv. | `extractLogOptions` makes the logger flags work before *or* after the subcommand and stops at `--`, so a wrapped binary's own flags are never eaten. `withDefaultVite` powers the bare-command passthrough described above: ```ts extractLogOptions(['node', 'dbx', 'sync', '--no-color']); // { argv: ['node', 'dbx', 'sync'], options: { color: false } } withDefaultVite(['node', 'dbx', 'fmt', '--check']); // ['node', 'dbx', 'vite', 'fmt', '--check'] ``` ## Config generator The generic Zod/env config engine is not a `dbx` command: a consumer builds an engine with `createConfigEngine` and registers the `config:*` commands on its **own** CLI. See [Config Generator](/guide/config-generator/). --- --- url: https://toolchain.deltabox.studio/guide/config.md --- # Configuration A project is driven by a single `dbx.config.ts`: ```ts [dbx.config.ts] import { defineConfig } from '@deltabox/toolchain'; export default defineConfig({ toolchain: { project: { type: 'monorepo-root', name: 'acme', scope: '@acme', }, ci: { ci: true, changelog: { branches: { main: 'main', dev: 'dev' }, commitlint: { scopes: ['api', 'web', 'deps'] }, }, }, tsconfig: { refs: true }, fmt: { vscode: true, oxc: { semi: true, singleQuote: true } }, lint: { oxc: { ignorePatterns: ['dist/**'] } }, vite: { test: { include: ['src/**/*.test.ts'] }, }, watch: { debounce: 150 }, }, }); ``` The `@deltabox/toolchain` settings live under the `toolchain` key, so a `dbx.config.ts` can host other dbx concerns alongside it. Each section has its own guide: [Project](/guide/project), [CI + Changelog](/guide/changelog/), [tsconfig](/guide/tsconfig), [Formatting & linting](/guide/formatting), [Vite+](/guide/vite), [Watch](/guide/watch), [Logger](/guide/logger) and [Providers](/guide/providers). ## Where to start * [Opt-in by presence](/guide/config/opt-in-by-presence) — which sections run, and how nested sections like `ci.changelog` and the individual workflows opt in. * [Nested configs](/guide/config/nested-configs) — how a workspace config merges over its root. * [`defineConfig`](/guide/config/defineconfig) — object, promise or callback. * [Config file names](/guide/config/config-file-names) — the file names the loader probes. * [Hooks](/guide/config/hooks) — lifecycle hooks in a `.ts`/`.js` config. * [Logging](/guide/config/logging) — the logger section and CLI overrides. * [Providers](/guide/config/providers) — provider passthrough colour settings. * [Vite+](/guide/config/vite) — the full Vite+ config and the generated shim. * [Programmatic API](/guide/config/programmatic-api) — load + sync from code. The generated, exhaustive section reference is [dbx.config](/config/reference). --- --- url: https://toolchain.deltabox.studio/guide/config/config-file-names.md --- # Config file names The loader looks for, in order: ``` dbx.config.ts dbx.config.mts dbx.config.js dbx.config.mjs dbx.config.cjs dbx.config.json ``` --- --- url: https://toolchain.deltabox.studio/guide/config/defineconfig.md --- # `defineConfig` `defineConfig` is an identity helper that gives the file full type-checking and autocomplete (it mirrors Vite's `defineConfig`). Like Vite, the argument may be a plain object, a promise, or a sync/async callback — the loader resolves it, so a config can do async work before returning `{ toolchain }`: ```ts [dbx.config.ts] export default defineConfig(async () => { const { readFile } = await import('node:fs/promises'); const version = (await readFile('./VERSION', 'utf-8')).trim(); return { toolchain: { project: { name: 'acme', nodeVersion: version }, }, }; }); ``` --- --- url: https://toolchain.deltabox.studio/guide/config/hooks.md --- # Hooks `.ts`/`.js` configs can declare lifecycle hooks (JSON cannot): ```ts [dbx.config.ts] export default defineConfig({ toolchain: { hooks: { afterLoad: ({ tokens }) => console.log(tokens), beforeSync: ctx => ctx.log('syncing…'), afterTask: ({ task, files }) => console.log(task.name, files?.length), afterSync: ({ result }) => console.log(result.written.length), onChange: ({ path }) => console.log('changed', path), }, }, }); ``` Return values are awaited. --- --- url: https://toolchain.deltabox.studio/guide/config/logging.md --- # Logging Every toolchain message goes through one logger. Configure it in the config: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { logger: { level: 'debug', color: false, name: 'dbx' }, }, }); ``` | Option | Values | Default | | ---------- | -------------------------------------------------- | ------- | | `level` | `debug` | `info` | `warn` | `error` | `silent` | `info` | | `color` | `true` | `false` | `true` | | `name` | prefix for non-indented lines | `dbx` | | `suppress` | id, message substring or `/regex/` (single/array) | none | | `logger` | custom `ToolchainLogger` implementation | none | `suppress` silences matching warnings/errors. A string matches an emitted id exactly or a message substring; a `/pattern/flags` string is treated as a regex. Ids are rendered in uppercase in the output (eg. `[FMT_CONFLICT]`), but match the lowercase id when suppressing. It accepts one value or an array: ```ts [dbx.config.ts] toolchain: { logger: { suppress: ['fmt.conflict', 'deprecated option', '/^Could not .* globally/'], }, } ``` Colour is **on by default** (the toolchain forces it rather than relying on TTY autodetection); `--no-color` or `logger: { color: false }` turn it off. Human output is rendered with [`@clack/prompts`](https://bombshell.dev/clack) — level symbols (`•` info, `◆` success, `▲` warn, `■` error) and a guide. ### Custom logger Pass any object implementing the agnostic `ToolchainLogger` interface (`raw`, `rawError`, `write`, `debug`, `info`, `success`, `warn`, `error`) to `logger.logger`. Level/suppression checks still apply before your methods run. ```ts [dbx.config.ts] const custom = { raw: (message = '') => process.stdout.write(`${message}\n`), rawError: (message = '') => process.stderr.write(`${message}\n`), write: () => {}, debug: () => {}, info: (message = '') => mySink.info(message), success: (message = '') => mySink.info(message), warn: (message = '') => mySink.warn(message), error: (message = '') => mySink.error(message), }; export default defineConfig({ toolchain: { logger: { logger: custom } }, }); ``` On the CLI, `--log-level ` and `--no-color` override the config (see the [CLI reference](/guide/cli)). Machine output (`--json`) is written unprefixed through the same logger. --- --- url: https://toolchain.deltabox.studio/guide/config/nested-configs.md --- # Nested configs A `dbx.config.*` in a **non-root** folder (a workspace, an app, a nested project) is merged over the nearest ancestor `dbx.config.*` found while walking up to the repository root (the walk stops at the `.git` boundary, so it never picks up a config from outside the repo). The merge is recursive: * **Objects** are merged key by key, so a child keeps every option it does not mention. * **Leaves** — booleans, strings, numbers and **arrays** — in the child **replace** the root's value (arrays are not concatenated). * **Repository-scoped sections are not inherited**: `ci`, `catalogs` and the top-level `changelog`, `tsconfig`, `vite.test` and `vite.run`. A workflow, workspace tsconfig tree, test config or task list describes the repository or one workspace, so a child starts without the parent's values for them. In particular, the root `tsconfig` task already expands every workspace `tsconfig.json`; inheriting it into each workspace would only re-scan that subtree. Everything else is shared unless the child overrides it — `vite.fmt`, `vite.lint`, `vite.plugins`, `logger`, `biome`, `templates`, `registry`, and so on — so lint/format settings are cohesive across a monorepo by default. ```ts [/dbx.config.ts] export default defineConfig({ toolchain: { logger: { level: 'debug', color: false }, vite: { fmt: { printWidth: 100, singleQuote: true }, }, }, }); // /packages/web/dbx.config.ts — inherits `vite.fmt`, tweaks `logger` export default defineConfig({ toolchain: { logger: { level: 'warn' }, // leaf overrides the root's level vite: { run: { tasks: { build: { command: 'tsdown' } } }, // not inherited }, }, }); ``` The workspace above ends up with `logger: { level: 'warn', color: false }` and the root's `vite.fmt` settings. Its `vite.run` holds only the `build` task it declares — the root's tasks are not merged in. > **Arrays replace, so repeat what you need.** If a workspace sets > `vite.fmt.ignorePatterns`, that array replaces the root's list (it is not > appended). Either keep the root patterns in the workspace array, or put the > shared pattern on the root config — for example `**/vite.config.ts` there > covers every workspace's generated shim. > **`project` is inherited except its identity fields.** `name`, `scope`, > `description` and `type` are per-project, so a non-root config keeps its own (or > falls back to its `package.json`) and is still detected from its own files — a > root `project` never renames a workspace. The shared fields (`author`, > `license`, `homepage`, `repo`, `packageManager`, ...) flow down so their > `package.json` values and replacement tokens stay shared. > `project: { inherit: false }` disables project inheritance entirely. `hooks` > are not inherited either — a parent's hooks are code and do not run for a > nested project's sync. --- --- url: https://toolchain.deltabox.studio/guide/config/opt-in-by-presence.md --- # Opt-in by presence Every section under `toolchain` is **opt-in by presence**: the toolchain only touches an area you actually ask for. * Omit a section — that area is left completely alone. * `section: true` — enable it with defaults (when the section has defaults). * `section: false` — explicitly disable it. * `section: { ...options }` — enable and configure it. This applies to nested sections too. `ci` is opt-in, and every workflow inside it (`ci.ci`, `ci.release`, `ci.issues`, `ci.changelog`, `ci.sync`) is opt-in as well: omit a workflow (or set it to `false`) and the toolchain will not generate or manage its file. Setting `ci: false` or `ci: { enabled: false }` disables the whole section, including every nested workflow and the changelog pipeline. The same rule backs `ci.changelog`: it is only managed when you declare it, and `ci.changelog: false` (or `enabled: false`) disables both its workflow and the changelog pipeline. Helpers `sectionEnabled(section)` and `sectionConfig(section)` implement this and are exported for programmatic use. The top-level `changelog` section configures the manual pipeline for `dbx changelog`/the API. It does not enable CI by itself; `ci.changelog` inherits its options when they are not overridden. See [Manual `toolchain.changelog`](/guide/changelog/#manual-toolchainchangelog). --- --- url: https://toolchain.deltabox.studio/guide/config/programmatic-api.md --- # Programmatic API ```ts import { loadToolchainConfig, runSync, defaultSyncTasks } from '@deltabox/toolchain'; const { config, tokens, projectType } = await loadToolchainConfig(process.cwd()); await runSync({ tasks: defaultSyncTasks(), cwd: process.cwd(), config, tokens, projectType }); ``` Full section reference: [dbx.config](/config/reference). --- --- url: https://toolchain.deltabox.studio/guide/config/providers.md --- # Providers Provider passthrough commands (Vite+) follow the logger's colour setting. The `providers` section can force provider colours on or off on its own: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { providers: { color: false }, }, }); ``` | Option | Values | Default | | ------- | ----------------- | -------------- | | `color` | `true` | `false` | inherit logger | `color: false` runs the wrapped binary with `NO_COLOR=1` and `FORCE_COLOR=0`, so it emits plain output even on a colour terminal. The global CLI `--no-color` flag turns provider colour off too. See [Providers](/guide/providers#colour). --- --- url: https://toolchain.deltabox.studio/guide/config/vite.md --- # Vite+ The whole Vite+ config lives in `toolchain.vite`. Every key is passed through verbatim to Vite+, so `dbx.config.*` stays the only hand-authored root config: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { vite: { test: { include: ['src/**/*.test.ts'], environment: 'node' }, lint: { ignorePatterns: ['dist/**'] }, fmt: { semi: true, singleQuote: true }, }, }, }); ``` `dbx sync` writes a thin root `vite.config.ts` that resolves that config at load time, so the generated file never duplicates (or drifts from) `dbx.config.*`: ```ts [vite.config.ts] import { defineConfig, type ViteUserConfig } from 'vite-plus'; // Generated by @deltabox/toolchain — edit the vite section in dbx.config and run `dbx sync`. import { resolveVite } from '@deltabox/toolchain/vite'; export default defineConfig(resolveVite({ cwd: import.meta.dirname })); ``` The shim imports `defineConfig` from whichever Vite package the project installs (`vite` or `vite-plus`, detected from `package.json`), so a plain `vite` project gets `import { defineConfig, type UserConfig } from 'vite'` and `resolveVite(...)`. `resolveVite(options?)` resolves the **same-directory** `dbx.config.*` (so a nested `vite.config.ts` uses the nested config it sits next to), or the `toolchain` you pass, then returns `toolchain.vite`. Pass `vite` inline to merge overrides over the loaded section (`resolveVite({ vite: { ... } })`). It is exported from `@deltabox/toolchain/vite` for programmatic use too. The single toolchain opt-out is `config`. Set `vite: { config: false }` to leave a hand-written `vite.config.ts` alone; set `config` to a string (relative to the project, or absolute) to write the managed shim at a different path instead. Everything else under `vite` is Vite+ config (plugins, `test`, `lint`, `fmt`, `run.tasks`, ...), so `toolchain.vite` is the full Vite+ configuration. The section is typed as Vite+'s own config (`ToolchainViteConfig` = Vite's `UserConfig` as augmented by Vite+), so the whole Vite+ surface autocompletes in `dbx.config.ts`. ### Static shims By default the shim calls `resolveVite` at load time (`shim: 'runtime'`). Set `vite: { shim: 'static' }` to serialize the resolved config into the shim instead: ```ts [vite.config.ts] import { defineConfig, type ViteUserConfig } from 'vite-plus'; export default defineConfig({ resolve: { tsconfigPaths: true }, run: { tasks: {/* ... */} }, }); ``` A static shim imports only `defineConfig`, so Vite+ can load it without a built `@deltabox/toolchain` (useful for source-first monorepos and bootstrapping). It must stay serializable: functions, regexps and class instances are rejected at sync time (plugins still work with `shim: 'runtime'`). Runtime shims import `@deltabox/toolchain/vite` by default. Set `vite.shimImport` to change that specifier, for example to a local wrapper that re-exports `resolveVite`. Relative values are resolved from the repository root and rewritten relative to each generated config: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { vite: { shimImport: './scripts/local-vite-resolver.ts', }, }, }); ``` This only affects `shim: 'runtime'` (the default); static shims have no toolchain import. Vite 8 resolves TypeScript `paths` through `resolve.tsconfigPaths`. Set it in `toolchain.vite.resolve` (and in Vitest configs) to reuse the tsconfig aliases: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { vite: { resolve: { tsconfigPaths: true } }, }, }); ``` For explicit aliases (or Vite versions without built-in `paths` support), use `resolveTsconfig()` from `@deltabox/toolchain/tsconfig` (see the [tsconfig guide](/guide/tsconfig#resolving-a-tsconfig)). Add the shim to `vite.fmt.ignorePatterns` so Oxfmt leaves the generated file alone - without it `vp fmt` would rewrite the shim and `dbx sync --check` would report drift. In a monorepo put `**/vite.config.ts` on the root config: workspace `fmt` settings are inherited, but a workspace's `ignorePatterns` array replaces the root's list. The default fmt ignores already include `**/vite.config.ts`; `dbx migrate --from vite` adds `/vite.config.ts` to `vite.fmt` only when the folded config already owns `fmt`. ## Format and lint defaults Opt in with `vite: { fmt: true }` / `vite: { lint: true }` to use the toolchain defaults (`DEFAULT_FMT_RULES` / `DEFAULT_LINT_RULES`, exported from `@deltabox/toolchain`). An options object is merged over those defaults, so `fmt: { semi: false }` keeps the shared ignores and print width and only changes `semi`. Shared ignore lists live in `DEFAULT_FMT_IGNORE_PATTERNS` / `DEFAULT_LINT_IGNORE_PATTERNS`, both layered over `DEFAULT_IGNORE_PATTERNS`. All three are exported (readonly) from `@deltabox/toolchain` and `@deltabox/toolchain/shared`, so you can compose your own list instead of repeating the defaults: ```ts [dbx.config.ts] import { DEFAULT_FMT_IGNORE_PATTERNS, defineConfig } from '@deltabox/toolchain'; export default defineConfig({ toolchain: { vite: { fmt: { ignorePatterns: [...DEFAULT_FMT_IGNORE_PATTERNS, '**/fixtures/**'], }, }, }, }); ``` Import sorting is enabled by default too. `sortImports` uses the usual groups (`builtin`, `external`, `internal`/`subpath`, relatives, styles) plus a `workspace` group whose patterns come from every `dbx.config.*` project in the repository, so consumer packages sort together without a hand-written `customGroups` entry. Override `sortImports: false` to disable it, or set your own groups — the resolved workspace group is still appended (and listed in `groups`) unless you disable sorting. To reuse the workspace group in your own `sortImports`, import the resolver: ```ts import { resolveFmtCustomGroups } from '@deltabox/toolchain'; const customGroups = await resolveFmtCustomGroups(); // [{ groupName: 'workspace', elementNamePattern: ['@acme/**', 'app', 'app/**', ...] }] ``` `resolveFmtCustomGroups()` reads the project graph, so it is safe to call from inside `dbx.config.*`. Pass `{ config: 'path/to/dbx.config.ts' }` to resolve a specific config instead of the discovered one, or an inline `project` to add a project the graph does not know about. In VS Code, set `deltabox.toolchain.enableFormatImportGroupDefaults` to `false` (user or workspace settings) to stop the extension from adding the workspace group. ## Oxc config files Vite+ reads `fmt`/`lint` from `vite.config.ts`, but the Oxc VS Code extension reads `.oxfmtrc.json` / `.oxlintrc.json`. When `vite.fmt` / `vite.lint` are defined, `dbx sync` writes those files as managed files so the editor picks up the same configuration. Only the root gets them by default; a nested `dbx.config.*` gets its own pair only when it defines `vite.fmt` / `vite.lint` itself (an inherited root config does not create per-workspace files). To adopt an existing hand-written `vite.config.ts`, fold it in with the migration (see [Migrations](/guide/migrations/#vite-config)): ```sh dbx migrate --from vite --write # folds the object into toolchain.vite (routes fmt/lint) dbx sync # writes the managed vite.config.ts shim ``` --- --- url: https://toolchain.deltabox.studio/guide/config-generator.md --- # Config Generator `@deltabox/toolchain/config` is a configuration engine. A **Zod/untyped schema is the single source of truth**: attach metadata to zod leaves with `.meta()`, then generate the env/config TypeScript types, a JSON Schema, a markdown reference, config metadata JSON, CLI option definitions and a CLI reference — all from that one schema. ## Where to start * [Describe a scope](/guide/config-generator/describe-a-scope) — write the Zod schema and its `.meta()`. * [Create the engine](/guide/config-generator/create-the-engine) — bind scopes, prefix, base group and root. * [Generate](/guide/config-generator/generate) — the generator methods. * [CLI commands](/guide/config-generator/cli-commands) — the `config:*` command descriptors. * [Load the config](/guide/config-generator/load-the-config) — the runtime loader (`parseEnvTree`, `compileZodSchema`, and `applyOverrides` from shared). * [Generated outputs](/guide/config-generator/generated-outputs) — types, markdown, env files, JSON Schema, CLI docs and formatting. * [Real-world shape](/guide/config-generator/real-world-shape) — how a consumer binds the engine end-to-end. ```ts import { createConfigEngine } from '@deltabox/toolchain/config'; ``` --- --- url: https://toolchain.deltabox.studio/guide/config-generator/cli-commands.md --- # CLI commands `configCommands({ engine, defaults })` returns `ToolchainCommand[]` (register them on your commander program with `@deltabox/toolchain/cli`): | Command | Writes | | ----------------- | -------------------------------------------- | | `config:env` | `.env.example` | | `config:types` | config types (`--env` also writes env types) | | `config:json` | `config.json` (metadata) | | `config:schema` | `config.schema.json` | | `config:markdown` | the markdown reference | | `config:cli` | `cli-options.json` | | `config:all` | everything, under one output directory | ```ts import { configCommands } from '@deltabox/toolchain/config'; const commands = configCommands({ engine, defaults: { typesDir: 'packages/shared/src/types', docsDir: 'docs/reference' }, }); // register `commands` on your commander program ``` The `config:*` commands generate config artifacts; they do not write the CLI reference. For that, pass the command descriptors your CLI registers to [`generateCliDocs()`](/guide/config-generator/generate#cli-docs). --- --- url: https://toolchain.deltabox.studio/guide/config-generator/create-the-engine.md --- # Create the engine Bind the schema, an env prefix, the base group and the project root once. A\nschema may be a Zod schema, an untyped Schema, an array, or a record keyed by\nscope: ```ts import { createConfigEngine } from '@deltabox/toolchain/config'; export const engine = createConfigEngine({ schema: { server: serverConfig, frontend: frontendConfig, cli: cliConfig, }, envPrefix: 'MYAPP_', baseGroup: 'base', // the flat fields hoisted out of the nested groups root: process.cwd(), // Optional: custom env-name -> dotted-path derivation (eg. dual prefixes). envPathOf: env => env .replace(/^(MYAPP_|VITE_)/, '') .toLowerCase() .replace(/_/g, '.'), }); ``` --- --- url: https://toolchain.deltabox.studio/guide/config-generator/describe-a-scope.md --- # Describe a scope Each scope is a Zod object. Leaves that are backed by an environment variable carry an `.meta()` describing that variable: ```ts import { z } from 'zod'; export const serverConfig = z.object({ base: z.object({ port: z.number().default(3000).meta({ env: 'MYAPP_PORT', description: 'HTTP port', }), host: z.string().default('0.0.0.0').meta({ env: 'MYAPP_HOST', description: 'Bind address', }), }), auth: z.object({ password: z.string().meta({ env: 'MYAPP_AUTH_PASSWORD', description: 'Admin password', required: true, secret: true, }), tokenTtl: z.number().default(3600).meta({ env: 'MYAPP_AUTH_TOKEN_TTL', description: 'Session lifetime (seconds)', }), }), }); ``` `ConfigMeta` fields: `envName`, `envPath` (dotted config path), `description`, `required`, `deprecated`, `deprecationMessage`, `example`, `since`, `see`, `remarks`, `experimental`, `alpha`, `beta`, `secret`, `scope`, `public` (safe to expose to the browser), `defaultValue` (when the resolved default differs from the env default), `cliFlag`, `cliFlagShort`, `typeName` and `id`. `env` / `path` remain supported as deprecated aliases. Markdown and type output show env/CLI equivalents by default; disable them with `includeEnv: false` / `includeCli: false`. `required: true` adds `@required` and `- **Required**` above the markdown `- **Type**` line. `deprecated: true` adds `@deprecated` / `- **Deprecated**`; `deprecationMessage` supplies the text. `example` (string or array) adds one `@example` tag/entry per value under `**Example(s):**`. `since` adds `@since` / `- **Since**`, `see` adds `@see` / `- **See**` links, `remarks` adds `@remarks` / `**Remarks:**`, and `experimental`/`alpha`/`beta` add their stability tags. Each generator accepts the matching `include*` flag (all default `true`) to suppress an annotation: `includeRequired`, `includeDeprecated`, `includeExamples`, `includeSince`, `includeSee`, `includeRemarks`, `includeExperimental`, `includeAlpha`, `includeBeta` — for example `generateConfigTypes(..., { includeExamples: false })` or `writeMarkdown(..., { includeDeprecated: false })`. --- --- url: https://toolchain.deltabox.studio/guide/config-generator/generate.md --- # Generate Every generator has two forms: * **Bound engine** — `engine.generateConfigTypes()` injects the engine's `envPrefix`, `baseGroup`, `root` and `envPathOf`. * **Standalone** — `generateConfigTypes(schema, options)` takes those options explicitly, for one-off scripts and tests. `write*` methods write to disk and return the written paths. The `generate*` methods return the content and only write when you pass `outputDir`/`output` (a string); `false` disables writing. ## At a glance | Output | Engine | Standalone | | -------------- | ------------------------------------------------------------ | --------------------------------------------------------- | | `.env.example` | `engine.generateEnvFile()` / `engine.writeEnvFile()` | `generateEnvFile()` | | Env types | `engine.generateConfigEnv()` / `engine.writeEnvTypes()` | `generateConfigEnv()` | | Config types | `engine.generateConfigTypes()` / `engine.writeConfigTypes()` | `generateConfigTypes()` | | Markdown | `engine.generateConfigMarkdown()` / `engine.writeMarkdown()` | `generateConfigMarkdown()` | | JSON Schema | `engine.generateJsonSchema()` / `engine.writeJsonSchema()` | `generateConfigJsonSchema()` / `generateJsonSchemaFile()` | | Metadata JSON | `engine.buildConfigJson()` | `buildConfigJson()` | | CLI options | `engine.generateConfigCli()` / `generateCliOptions()` | `generateCliOptions()` | | CLI docs | — | `generateCliDocs()` | | Formatting | — | `formatGenerated()` | ## Env file — `.env.example` ```ts engine.writeEnvFile({ output: '.env.example' }); ``` Standalone, with the common options: ```ts import { generateEnvFile } from '@deltabox/toolchain/config'; const content = generateEnvFile(schema, { envPrefix: 'MYAPP_', header: 'Copy to .env and fill the blanks.', scopes: ['server', 'cli'], exclude: ['MYAPP_INTERNAL_TOKEN'], values: { MYAPP_PORT: '8080' }, extra: { MYAPP_FEATURE_FLAG: 'true' }, }); ``` Options: `include`/`exclude` (env names), `values` (default overrides), `scopes`, `bare`, `extra` (raw `NAME=value` lines), `header`, `scopeSeparator`. Secrets are emitted commented out. ## Env types — `env.ts` + `env.d.ts` ```ts engine.writeEnvTypes({ outputDir: 'types' }); // types/env.ts, types/env.d.ts ``` ```ts import { generateConfigEnv } from '@deltabox/toolchain/config'; const files = generateConfigEnv(schema, { envPrefix: 'MYAPP_', outputDir: 'types', envInterfaceName: 'AppEnv', viteEnvInterfaceName: 'AppViteEnv', importPath: './env.js', augmentVite: true, envComment: 'Runtime configuration.', }); // files: { 'env.ts': '...', 'env.d.ts': '...' } ``` Options: `envFile`/`dtsFile` (file names), `header`, `envHeader`, `dtsHeader`, `augmentVite` (a `declare module 'vite'` augmentation). ## Config types ```ts engine.writeConfigTypes({ outputDir: 'types' }); ``` Standalone, with hand-written overrides and aliases: ```ts import { generateConfigTypes } from '@deltabox/toolchain/config'; const files = generateConfigTypes(schema, { baseGroup: 'base', outputDir: 'types', interfaceNames: { server: 'ServerConfig' }, aliases: { server: 'export type ServerOptions = ServerConfig;' }, imports: ["import type { FastifyServerOptions } from 'fastify';"], overrides: { ServerConfig: `export interface ServerConfig { host?: string; port?: number; }`, }, header: '// Generated — do not edit.', extension: 'ts', includeEnv: false, }); ``` Options: `interfaceNames` (per scope), `aliases` (per scope, appended), `overrides` (full declaration text, keyed by type name), `imports` (emitted once at the top), `extension`, `header`, and the `include*` annotation flags. ## Markdown ```ts engine.writeMarkdown({ outputDir: 'docs/reference/configuration' }); ``` ```ts import { generateConfigMarkdown } from '@deltabox/toolchain/config'; const files = generateConfigMarkdown(schema, { baseGroup: 'base', envPrefix: 'MYAPP_', outputDir: 'docs/reference/configuration', title: 'Configuration', frontmatter: ctx => `title: ${ctx.options.title ?? 'Configuration'}`, combined: true, // one index.md instead of one file per scope includeEnv: false, }); ``` `frontmatter: false` omits the frontmatter, a string replaces the body, and a callback builds it from the generator options. `combined: true` renders every scope into one file. `cliScope`, `shortFlags` and `references` control the CLI equivalents and footer links. ## JSON Schema ```ts const doc = await engine.generateJsonSchema(); await engine.writeJsonSchema({ output: 'config.schema.json' }); ``` ```ts import { generateConfigJsonSchema, generateJsonSchemaFile } from '@deltabox/toolchain/config'; const doc = await generateConfigJsonSchema(schema, { baseGroup: 'base', id: 'https://acme.dev/schemas/config.json', title: 'Acme configuration', description: 'Every env-backed setting.', }); const path = await generateJsonSchemaFile(schema, { output: 'config.schema.json' }); ``` Both functions are async. `generateConfigJsonSchema` returns the document; `generateJsonSchemaFile` writes it and returns the path. ## Config metadata JSON ```ts const metadata = engine.buildConfigJson(); fs.writeFileSync('config.json', `${JSON.stringify(metadata, null, 4)}\n`); ``` ```ts import { buildConfigJson } from '@deltabox/toolchain/config'; const metadata = buildConfigJson(schema, { envPrefix: 'MYAPP_', comment: 'Generated from the config schemas.', }); ``` The document contains `$comment`, `version`, and `env[]` entries with the name, path, scope, description, default, `required`, `secret` and `public` flags. Secrets never include a default. ## CLI options ```ts const defs = engine.generateConfigCli({ scope: 'cli' }); // engine.generateCliOptions() is an alias. ``` ```ts import { createCliArgsParser, generateCliOptions } from '@deltabox/toolchain/config'; const defs = generateCliOptions(schema, { envPrefix: 'MYAPP_', scope: 'cli', output: 'cli-options.json', }); const parser = createCliArgsParser(defs); parser.parse(['--base-url', 'http://localhost:8080']); ``` Options: `scope` (default `cli`), `output`, `shortFlags`, `envPathOf`. ## CLI docs `generateCliDocs(commands, options?)` renders a markdown reference from `ToolchainCommand` descriptors. It is standalone because it does not read a config schema: ```ts import { builtinCommands } from '@deltabox/toolchain/cli'; import { generateCliDocs } from '@deltabox/toolchain/config'; const markdown = generateCliDocs([...builtinCommands(), ...myCommands], { title: 'CLI reference', intro: 'Generated from the registered commands.', prefix: 'my-cli', outline: [2, 3], }); ``` Hidden commands are omitted, duplicate names keep the first descriptor, and args/options/defaults are rendered like the `dbx` reference. Pass the result through `formatGenerated(..., { fileName: 'cli.md' })` to format it. ## Formatting ```ts import { formatGenerated } from '@deltabox/toolchain/config'; const formatted = await formatGenerated(source, { fileName: 'config.ts', // infers the language options: { printWidth: 100 }, // merged over GENERATED_FORMAT_OPTIONS }); ``` `formatGenerated` is best-effort: it returns the input unchanged when no formatter is available. ## Advanced ### Post-process generated output Generators return strings or records, so you can transform them before writing: ```ts import fs from 'node:fs'; import { formatGenerated, generateConfigTypes } from '@deltabox/toolchain/config'; const files = generateConfigTypes(schema, { baseGroup: 'base' }); for (const [scope, source] of Object.entries(files)) { const withBanner = `// Generated from schemas/${scope}.ts — do not edit.\n${source}`; const formatted = await formatGenerated(withBanner, { fileName: `${scope}.ts` }); fs.mkdirSync('types', { recursive: true }); fs.writeFileSync(`types/${scope}.ts`, formatted); } ``` ### Custom headers, imports and overrides ```ts generateConfigTypes(schema, { header: '/* eslint-disable */', imports: ["import type { AppEnv } from './env.js';"], overrides: { ServerConfig: 'export interface ServerConfig extends BaseServer {}', }, }); ``` `overrides` is keyed by the generated type name and replaces the whole declaration, so keep the `imports`/`header` options handy for types the schema cannot express. ### Dry runs Every generator accepts `outputDir: false` / `output: false`: ```ts generateConfigTypes(schema, { outputDir: false }); await generateConfigJsonSchema(schema, { output: false }); ``` The engine's `write*` methods always write; use the `generate*` methods when you only need the content. ### Engine vs standalone The engine form injects the bound options; the standalone form takes them explicitly. They produce the same output: ```ts const engine = createConfigEngine({ schema, envPrefix: 'MYAPP_', baseGroup: 'base', }); engine.generateConfigMarkdown(); // uses the bound options generateConfigMarkdown(schema, { envPrefix: 'MYAPP_', baseGroup: 'base', }); ``` See [Generated outputs](/guide/config-generator/generated-outputs) for the shape of each file and [CLI commands](/guide/config-generator/cli-commands) for the `config:*` descriptors. --- --- url: https://toolchain.deltabox.studio/guide/config-generator/generated-outputs.md --- # Generated outputs Every generator reads from the same Zod or untyped schema. The engine's own config model is the living example: its types are committed at [packages/shared/src/config-types.ts](https://github.com/deltabox-studio/toolchain/blob/main/packages/shared/src/config-types.ts). For the engine/standalone call forms and the full option list per generator, see [Generate](/guide/config-generator/generate). ## Config types — `generateConfigTypes()` / `writeConfigTypes()` Named `interface`s (and `type` aliases for enums/unions) with jsdoc taken from `.describe()`. Unions and refs stay named, so nested objects are referenced rather than inlined: ```ts // engine.generateConfigTypes() -> { server: "...", cli: "..." } engine.writeConfigTypes({ outputDir: 'types' }); // types/server.ts, types/cli.ts ``` ```ts // types/server.ts export interface ServerConfig { /** Bind host. */ host?: string; port?: number; auth?: { password?: string; enabled?: boolean }; } ``` An object schema that also accepts arbitrary keys (Zod `.catchall(...)`, `.loose()` or `z.looseObject(...)`, ie. `additionalProperties` in the JSON Schema) generates an open interface with an index signature, so the extra keys stay typed as `unknown` instead of being rejected: ```ts export interface ServerConfig { /** Bind host. */ host?: string; [key: string]: unknown; } ``` ## Markdown — `writeMarkdown()` One reference page per scope plus an index listing the required/core env vars: ```ts engine.writeMarkdown({ outputDir: 'docs/reference/configuration' }); // docs/reference/configuration/server.md, .../index.md ``` See a real full example at [config/reference.md](../../config/reference.md). Markdown frontmatter is enabled by default. Set `frontmatter: false` to omit it, pass a raw string to replace the body, or pass a callback to build it from the generator options. The returned string is wrapped in `---` automatically. ```ts generateConfigMarkdown(schema, { frontmatter: ctx => `title: ${ctx.options.title ?? 'Configuration'}`, }); ``` Use `combined: true` to render every scope into one file instead of one file per scope. `generateConfigTypes`, `generateConfigMarkdown` and `generateConfigEnv` return file contents and only write when `outputDir`/`output` is a string; `false` explicitly disables writing. ## Env — `renderEnvFile()` / `writeEnvFile()` A `.env.example`; secrets are emitted commented out so operators fill them in: ```sh engine.writeEnvFile({ output: '.env.example' }); # ---- SERVER ---- MYAPP_HOST=localhost # MYAPP_AUTH_PASSWORD= ``` ## JSON Schema — `generateJsonSchema()` / `writeJsonSchema()` The resolved untyped JSON Schema for every scope, for editor autocomplete: ```ts await engine.writeJsonSchema({ output: 'config.schema.json' }); ``` ## CLI docs — `generateCliDocs()` `generateCliDocs` is the one generator that does not read the config schema: it renders a markdown reference from `ToolchainCommand` descriptors. Pass the command list your CLI actually registers, including custom descriptors: ```ts import { builtinCommands } from '@deltabox/toolchain/cli'; import { generateCliDocs } from '@deltabox/toolchain/config'; const markdown = generateCliDocs([...builtinCommands(), ...myCommands], { prefix: 'my-cli', title: 'CLI reference', intro: 'Generated from the registered commands.', }); ``` Hidden commands are omitted and duplicate names keep the first descriptor. The `dbx` reference itself is generated this way, so a custom command no longer has to be added to `builtinCommands()` to be documented. ## Post-processing The `generate*` functions return their content, so you can transform it before writing (the `write*` methods write immediately): ```ts const files = generateConfigTypes(schema); for (const [scope, source] of Object.entries(files)) { const sourceWithBanner = `// Generated — do not edit.\n${source}`; await fs.promises.writeFile(`types/${scope}.ts`, sourceWithBanner); } ``` If the transform changes the formatting, run the result through [`formatGenerated()`](#formatting--formatgenerated) before writing. ## Formatting — `formatGenerated()` Generated text is formatted **at generation time**, so a written file is already formatted instead of relying on a separate `yarn format` pass. `formatGenerated` runs source through Oxfmt, preferring the project-local `vite-plus/fmt` (Oxfmt ships with Vite+), then `oxfmt`, and returns the input untouched when no formatter is resolvable, so generation never fails on a missing formatter. ```ts import { formatGenerated, GENERATED_FORMAT_OPTIONS } from '@deltabox/toolchain/config'; const source = 'export const config={a:1}'; const formatted = await formatGenerated(source, { fileName: 'config.ts', // infers the language (default `generated.ts`) options: { printWidth: 100 }, // merged over GENERATED_FORMAT_OPTIONS }); ``` `GENERATED_FORMAT_OPTIONS` is the baseline Oxfmt config matching the toolchain's own `vite.config.ts` (4-space indent, single quotes, `trailingComma: 'es5'`, `jsdoc: true`, ...). Pass `options.fileName` to format JSON, markdown or another language, and `options.options` to override any single option. For a full, real example of the generated config types, see `packages/shared/src/config-types.ts`. --- --- url: https://toolchain.deltabox.studio/guide/config-generator/load-the-config.md --- # Load the config at runtime Generation is only half of it. The same schema backs a small, dependency-free **loader**. `parseEnvTree` folds `PREFIX_*` variables into a nested tree (splitting the remainder on `_`), and the typed accessors read dotted paths out of it: ```ts import { envBool, envInt, envList, envMap, envString, parseEnvTree, } from '@deltabox/toolchain/config'; import { applyOverrides } from '@deltabox/toolchain/shared'; // MYAPP_HOST, MYAPP_PORT, MYAPP_LOG_PRETTY, ... -> a nested tree const tree = parseEnvTree(process.env, 'MYAPP_'); const config = { host: envString(tree, 'host', '0.0.0.0'), port: envInt(tree, 'port', 3000), pretty: envBool(tree, 'log.pretty', true), corsOrigins: envList(tree, 'cors.origin', []), headers: envMap(tree, 'response.headers.map'), // `Key=value,...` -> record }; ``` | Helper | Reads | | ---------------------------- | ---------------------------------------------------- | | `envString(tree, path, fb?)` | string (unset/empty -> fallback) | | `envInt` / `envBool` | number / boolean (`true/1/yes/on`, `false/0/no/off`) | | `envList` | comma-separated list | | `envMap` | `Key=value,...` into a lowercase-keyed record | | `readEnvPath(tree, path)` | the raw value at a dotted path | Layer typed overrides on top of the env values (a config file, then CLI flags) with `applyOverrides`, then validate the result through the scope schema so it is guaranteed to match the generated types: ```ts import { compileZodSchema } from '@deltabox/toolchain/config'; const schemaCompiler = compileZodSchema(serverConfig); export function loadConfig(env = process.env, file = {}, cli = {}) { const tree = parseEnvTree(env, 'MYAPP_'); const fromEnv = { base: { host: envString(tree, 'host', '0.0.0.0'), port: envInt(tree, 'port', 3000), }, auth: { password: envString(tree, 'auth.password', ''), tokenTtl: envInt(tree, 'auth.token.ttl', 3600), }, }; return schemaCompiler.parse(applyOverrides(fromEnv, file, cli)); } ``` A resolver like this is the runtime counterpart of the generators — same schema, same defaults, one typed object out. > The dotted path is the env name with the prefix removed and `_` folded to `.`, > so `MYAPP_AUTH_TOKEN_TTL` reads as `auth.token.ttl`. When a field name differs > from its env-derived path, map it explicitly in the resolver (as above). --- --- url: https://toolchain.deltabox.studio/guide/config-generator/real-world-shape.md --- # Real-world shape Proxified binds three scopes under a `PROXIFIED_` prefix and reuses the engine for every generator: ```ts import { createConfigEngine } from '@deltabox/toolchain/config'; export const configEngine = createConfigEngine({ schema: { server: serverConfig, frontend: frontendConfig, cli: cliConfig, }, envPrefix: 'PROXIFIED_', baseGroup: 'base', root: monorepoRoot(), envPathOf: env => envPathOf(env), }); ``` The generators then run from a `config:generate-all` script (built on the `configCommands({ engine })` helpers) to keep `.env.example`, the JSON Schema, the markdown reference, the types and the CLI options in sync with the schemas. These `config:*` commands belong to the consumer's own CLI, `dbx` itself does not expose them. --- --- url: https://toolchain.deltabox.studio/guide/formatting.md --- # Formatting & linting `toolchain.fmt` and `toolchain.lint` select a formatter/linter, hold its options, and optionally add project-scoped VS Code defaults: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { fmt: { vscode: true, // add missing VS Code formatter settings (default false) oxc: { printWidth: 100, singleQuote: true }, biome: { formatter: { indentWidth: 4 } }, }, lint: { vscode: true, oxc: { rules: { eqeqeq: 'warn' } }, eslint: { rules: {} }, }, }, }); ``` The supported tools are: * `fmt`: `oxc` (Oxfmt), `biome`, `prettier`, `dprint`. * `lint`: `oxc` (Oxlint), `biome`, `eslint`. Each tool accepts `true` (use the defaults) or `false` (disable that tool), in addition to its options object. The Oxfmt/Oxlint option types are generated from the tools' official JSON schemas (vendored in the package), so editor completion and the generated config reference match the tool. The shared ignore lists are exported from `@deltabox/toolchain` and `@deltabox/toolchain/shared`: `DEFAULT_IGNORE_PATTERNS` (build/cache dirs), and `DEFAULT_FMT_IGNORE_PATTERNS` / `DEFAULT_LINT_IGNORE_PATTERNS` (the Oxc defaults, layered over `DEFAULT_IGNORE_PATTERNS`). Import them to extend the defaults rather than re-listing them. Oxfmt import sorting is enabled in the defaults as well. `sortImports` includes a `workspace` group built from every `dbx.config.*` project's scope/name; `resolveFmtCustomGroups()` returns that group so you can reuse it in a custom `sortImports` config. Set `sortImports: false` to opt out. ## Shorthand opt-in `fmt: true` / `lint: true` (or an options object with only `vscode`) picks a tool automatically. `dbx sync` resolves it in this order: 1. the project's `.vscode/settings.json`; 2. the nearest `package.json` or tooling config file, walking up to the repo root (a workspace-specific tool beats a root-installed one); 3. the VS Code user settings language blocks; 4. the Oxc defaults. Each level prefers a tool's own config file (`.oxfmtrc.json`, `biome.json`, `.prettierrc`, `dprint.json`, `.eslintrc.*`, ...) before its `package.json` dependency, and honours the formatter/linter precedence. If nothing is found, sync warns (`fmt.no-tool` / `lint.no-tool`) and still applies the Oxc defaults. Existing user-owned config files are not overwritten blindly: the detected tool's options are read into the matching `toolchain.fmt.*` / `toolchain.lint.*` entry. Oxc options are merged over the toolchain defaults using the same mapping as [`dbx migrate`](/guide/migrations/). ## Running the active tool `dbx fmt` / `dbx lint` run the tool the config (or discovery) selects, even when `toolchain.fmt` / `toolchain.lint` is omitted: * an explicit `toolchain.fmt.*` / `toolchain.lint.*` tool wins, in precedence order; * otherwise the same discovery `dbx sync` uses picks a tool from the project `.vscode/settings.json`, the nearest config file/`package.json`, or the VS Code user settings; * with nothing configured or installed, they fall back to `vp fmt` / `vp lint` (Oxfmt / Oxlint). Mode flags are translated per tool (`--check`/`--write` for formatters, `--fix` for linters) and other operands are forwarded. Oxc keeps the full Vite+ passthrough, so `dbx vite fmt ...` still accepts every `vp` flag. ## Precedence When more than one formatter/linter is configured, editor defaults use: * **formatters**: `oxc` > `biome` > `prettier` > `dprint`. * **linters**: `oxc` > `biome` > `eslint`. `dbx sync` warns (`fmt.conflict` / `lint.conflict`) when this happens; silence it with [`logger.suppress`](/guide/config/logging). `toolchain.vite.fmt` / `toolchain.vite.lint` are the Oxc config written by Vite+, and **override** `fmt.oxc` / `lint.oxc` with a deep merge (scalars, arrays and booleans in `vite.*` win). With both: ```ts toolchain: { fmt: { oxc: { semi: false, singleQuote: true } }, vite: { fmt: { singleQuote: false } }, } // effective Oxfmt options: { semi: false, singleQuote: false } ``` ## Generated config files * `fmt.oxc` / `lint.oxc` (+ `vite.fmt` / `vite.lint`) write managed `.oxfmtrc.json` / `.oxlintrc.json`. * `fmt.biome` / `lint.biome` merge into the managed `biome.json`. * The deprecated top-level `biome` section still works; prefer `fmt.biome` / `lint.biome`. Oxc files are root-only unless a nested `dbx.config.*` defines `fmt.oxc` / `lint.oxc` / `vite.fmt` / `vite.lint` itself. Biome keeps its existing per-project behaviour. When more than one formatter or linter is configured, sync warns (`fmt.conflict` / `lint.conflict`) because the generated config files and editor defaults may conflict. ## VS Code editor defaults Set `fmt.vscode` and/or `lint.vscode` (both default `false`) to add missing project-scoped settings to `.vscode/settings.json`. The generated defaults are deliberately small: * `editor.defaultFormatter` * language blocks (`[javascript]`, `[typescript]`, ...) * `editor.codeActionsOnSave` (lint only) Use an options object to override or exclude individual keys: ```ts toolchain: { fmt: { oxc: {}, vscode: { overrides: { 'editor.defaultFormatter': 'oxc.oxc-vscode', '[typescript]': { 'editor.defaultFormatter': 'esbenp.prettier-vscode' }, }, exclude: ['[javascript]'], }, }, lint: { oxc: {}, vscode: true }, } ``` These editor defaults are **not tracked** and existing keys are never overridden. (The separate Conventional Commits/Biome VS Code sync may track `.vscode/settings.json` when those sections are enabled.) Remove or change the generated keys freely. Setting `vscode: false` (or omitting it) adds nothing. A nested project scaffolded below a root that already has `.oxfmtrc.json` / `.oxlintrc.json` omits its own Oxc files and `fmt.oxc` / `lint.oxc` opt-ins, so `dbx fmt` / `dbx lint` keep using the root configuration. ## Migrating ```sh dbx migrate --from oxc --write # .oxfmtrc/.oxlintrc -> fmt.oxc/lint.oxc dbx migrate --from biome --from prettier --write dbx migrate --from all --write ``` Unless `--target` is given, the migrated formatter/linter options go to `toolchain.vite.fmt`/`toolchain.vite.lint` when the existing Vite config already defines `fmt`/`lint`; otherwise they go to the top-level `toolchain.fmt.oxc`/`toolchain.lint.oxc`. See [Migrations](/guide/migrations/). ## Biome > **Deprecated:** prefer `toolchain.fmt.biome` / `toolchain.lint.biome` (see > above). The top-level `biome` section still works and still writes > `biome.json`. The `biome` section generates a `biome.json` for consumers that still want a Biome config. The toolchain itself is built on **Oxc** (Oxfmt + Oxlint) through Vite+, but a repo that runs Biome directly can let the toolchain manage its `biome.json` from the same `dbx.config.*`. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { biome: true, // or { overrides: { ... } } }, }); ``` `dbx sync` writes `biome.json`, resolving the `{{name}}` / `{{scope}}` replacement tokens and merging your overrides over the defaults. If any other formatter/linter is also configured, sync still writes both the Biome and Oxc config files, but warns (`fmt.conflict` / `lint.conflict`) that the two tools may conflict. Silencing it is possible with `logger: { suppress: ['fmt.conflict'] }`; prefer one toolchain per repo. ### Options | Option | Type | Default | Description | | ----------- | ------------------------- | -------------------- | -------------------------------------------- | | `enabled` | `boolean` | enabled when defined | Master switch. | | `vscode` | `boolean` | `true` | Write the Biome VS Code settings/extensions. | | `overrides` | `Record` | — | Deep-merged over the generated `biome.json`. | #### `overrides` Overrides are merged into the generated config **recursively**, and **arrays are unioned** (not replaced) — so `files.includes` can be extended: ```ts biome: { overrides: { files: { includes: ['!**/generated/**'] }, formatter: { indentWidth: 2 }, }, } ``` Default array entries can be pruned with a `$remove:` entry (its value is JSON, or a raw string when it is not valid JSON) or replaced wholesale with `$reset`: ```ts import { BIOME_RESET, removeBiomeEntry } from '@deltabox/toolchain/biome'; biome: { overrides: { files: { includes: [ removeBiomeEntry('!**/.generated'), // drop a default '!**/generated/**', ], }, assist: { actions: { source: { organizeImports: { options: { groups: [BIOME_RESET, ['@/lib/**']] }, }, }, }, }, }, } ``` `removeBiomeEntry(entry)` builds the directive, and nested arrays (eg. import groups) are matched deeply. The same directives work in `fmt.biome` / `lint.biome`; sources are applied in order, so later ones see earlier results. #### `vscode` When on (the default), the sync also writes the recommended Biome VS Code settings and the Biome extension recommendation (see [VS Code Extension](/guide/vscode)). Set `biome: { vscode: false }` to skip them. ### Programmatic API ```ts import { BIOME_RESET, biomeConfig, biomeSyncTask, biomeVscodeSettings, mergeBiomeConfig, removeBiomeEntry, renderBiomeConfig, renderBiomeJson, resolveBiomeArray, } from '@deltabox/toolchain/biome'; ``` | Export | Description | | ----------------------------------------- | -------------------------------------------------------- | | `biomeSyncTask()` | The sync task (writes `biome.json`). | | `biomeConfig` / `biomeVscodeSettings` | The generic Biome config / recommended VS Code settings. | | `renderBiomeConfig(tokens, overrides?)` | Resolve tokens + merge overrides into an object. | | `renderBiomeJson(tokens, overrides?)` | Serialize the rendered config. | | `mergeBiomeConfig(base, patch)` | The recursive/array-union merge used above. | | `resolveBiomeArray(base, patch)` | Resolve `$remove:`/`$reset` directives for one array. | | `removeBiomeEntry(entry)` / `BIOME_RESET` | Build the array-pruning / array-reset directives. | > Migrating **from** Biome to Oxc is a different concern — see > [Migrations](/guide/migrations/). --- --- url: https://toolchain.deltabox.studio/guide/logger.md --- # Logger Every toolchain message goes through one logger, so output stays consistent and respects a single configuration. The `logger` section controls its level, colours and prefix. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { logger: { level: 'debug', color: false, name: 'dbx' }, }, }); ``` ## Options | Option | Type | Default | Description | | ---------- | -------------------------------------------------- | ------- | ------------------------------------------------------ | | `level` | `debug` | `info` | `warn` | `error` | `silent` | `info` | Minimum level to print. | | `color` | `boolean` | `true` | Force colours on/off. | | `name` | `string` | `dbx` | Prefix for non-indented lines. | | `suppress` | `string \| string[]` | none | Silence warnings/errors by id, substring or `/regex/`. | | `logger` | `ToolchainLogger` | none | Custom logger implementation. | Colour is **on by default** (the toolchain forces it rather than relying on TTY autodetection); `--no-color` or `logger: { color: false }` turn it off. Human output is rendered with [`@clack/prompts`](https://bombshell.dev/clack) — level symbols (`•` info, `◆` success, `▲` warn, `■` error) and a guide. ## Custom logger `logger.logger` accepts a `ToolchainLogger` (`raw`, `rawError`, `write`, `debug`, `info`, `success`, `warn`, `error`). The toolchain still applies `level` and `suppress` before delegating, so a custom sink receives only visible messages. ```ts [dbx.config.ts] toolchain: { logger: { logger: { raw: m => process.stdout.write(`${m}\n`), rawError: m => process.stderr.write(`${m}\n`), write: () => {}, debug: () => {}, info: m => sink.info(m), success: m => sink.info(m), warn: m => sink.warn(m), error: m => sink.error(m), }, }, } ``` ## CLI overrides | Flag | Description | | ------------------- | ---------------------------------------------------------- | | `--log-level ` | Minimum level: `debug`, `info`, `warn`, `error`, `silent`. | | `--color` | Force coloured output (the default). | | `--no-color` | Disable coloured output. | They win over the config and may appear before or after the subcommand. ## Machine output `--json` (and provider passthroughs) write raw, unprefixed, undecorated output through the same logger (`logger.raw`), so it is safe to pipe into `jq` or another tool. ## Providers Provider passthrough commands (Vite+) follow the logger's colour setting, and the [`providers`](/guide/providers#colour) section can force provider colours on or off independently. `color: false` runs the wrapped binary with `NO_COLOR=1` and `FORCE_COLOR=0`. ## Programmatic API ```ts import { configureLogger, createLogger, getLogger, isLogLevel, logger, normalizeLogLevel, setLogConfig, setLogOverrides, } from '@deltabox/toolchain/shared'; ``` | Export | Description | | ---------------------------------- | ------------------------------------------------------ | | `logger` | The active logger (delegates to the current instance). | | `createLogger(options?)` | Build a logger with explicit options. | | `setLogConfig(section)` | Apply the `logger` config section. | | `setLogOverrides(options)` | Apply CLI overrides (win over the config). | | `configureLogger(options?)` | Alias for `setLogOverrides`. | | `isLogLevel` / `normalizeLogLevel` | Validate/coerce a level. | Use `logger.info` for human messages and `logger.raw` for machine output; avoid `console.log` so output stays consistent and respects the config and CLI flags. --- --- url: https://toolchain.deltabox.studio/guide/migrations.md --- # Migrations `dbx migrate` converts a project's tooling config from another tool to **Oxc** (Oxfmt + Oxlint), or folds an existing Oxc/Vite config into `dbx.config.*`. Migrations are pluggable — adding a source tool is a matter of implementing one interface. ```sh dbx migrate:list # what's available + detected dbx migrate --from biome # dry-run dbx migrate --from biome --write # writes the oxc config dbx migrate --from biome --target vite --write dbx migrate --from biome --from prettier --write # several sources dbx migrate --from oxc --write # fold .oxfmtrc/.oxlintrc into dbx.config dbx migrate --from all # every detected source dbx migrate --check # CI: exit 1 when the oxc config is stale dbx migrate --from eslint --online --write # flat config via @oxlint/migrate ``` `dbx migrate` is a dry-run unless you pass `--write` (or `--check`). `--from` is repeatable; `--from all` (or omitting it) selects every detected source. ## Sources | Source | Notes | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `biome` | Formatter options, `files.includes` negations → `ignorePatterns`, **`organizeImports.groups` → Oxfmt `sortImports`**, and a curated Biome → Oxlint rule map. | | `prettier` | Prettier-compatible options transfer directly; Tailwind/PackageJson plugins detected; `.prettierignore` → `ignorePatterns`. | | `eslint` | JSON configs map rule names + severities (`@typescript-eslint/*` → `typescript/*`); JS/flat configs use `@oxlint/migrate` with `--online --write`. | | `dprint` | `dprint.json` layout + TypeScript options → Oxfmt. | | `oxc` | Folds `.oxfmtrc.json` / `.oxlintrc.json` into `dbx.config.*`'s `toolchain.fmt.oxc` / `toolchain.lint.oxc`; `dbx sync` manages them again. | | `vite` | Folds a hand-written `vite.config.*` into `dbx.config.*`'s `toolchain.vite` (see below). | ## Targets * `--target files` (default) — writes `.oxfmtrc.json` and `.oxlintrc.json`. * `--target vite` — folds the result into an existing `vite.config.ts` (inserting/replacing `fmt` and `lint`), which is what Vite+ reads. * The `oxc` source targets `dbx.config.*` (`toolchain.fmt.oxc` / `toolchain.lint.oxc`, or `toolchain.vite.fmt/lint` when `--target vite`). Unless `--target` is given, formatter/linter migrations route by what the existing Vite config already owns: * If `vite.config.*` (or an existing `toolchain.vite`) defines `fmt`/`lint`, the migrated options are merged into `toolchain.vite.fmt`/`toolchain.vite.lint`, and no top-level `toolchain.fmt`/`toolchain.lint` is created. * Otherwise they are folded into the top-level `toolchain.fmt.oxc` / `toolchain.lint.oxc`, and `vite.fmt`/`vite.lint` are left undefined. ## Recursive migration By default `dbx migrate` only touches the root (`--cwd`, or the current directory). `--recursive` walks the discovered projects under the root and migrates each one that has a **detected source**: ```sh dbx migrate --from biome --from prettier --recursive --write ``` On an interactive terminal it first asks which projects to migrate with a multi-select — every candidate is listed by its relative path (with its package name as a hint), plus an **All projects** option. Non-interactive runs (piped output, CI, or `--json`) migrate every candidate without prompting. A project is a candidate when a migration matching `--from` (or any migration, for `all`/no `--from`) is detected in its directory — so a workspace with its own `biome.json` or `vite.config.*` is included, and one without is skipped. ## `vite.config` The `vite` source moves a hand-written `vite.config.*` into `dbx.config.*`'s `toolchain.vite`, so the whole Vite+ config lives in the one hand-authored config and `dbx sync` writes a thin `vite.config.ts` shim that resolves it at load time: ```sh dbx migrate --from vite # dry-run: shows what would fold in dbx migrate --from vite --write # folds the object into toolchain.vite dbx sync # writes the managed vite.config.ts shim ``` When the folded config already owns `fmt`, it adds `/vite.config.ts` to `toolchain.vite.fmt.ignorePatterns`, so Oxfmt leaves the generated shim alone (otherwise `vp fmt` would rewrite it and `dbx sync --check` would report drift). When it does not, `vite.fmt` stays undefined; the default fmt ignores already cover `**/vite.config.ts`. It extracts the object literal passed to `defineConfig(...)` (or the default-exported object) with a string-aware, balanced-paren scanner and text-splices it into the nearest `dbx.config.*`: it inserts `vite: { ... }`, or **deep-merges** into an existing `toolchain.vite` (nested objects are combined; arrays and scalars from the vite config win), so its settings are kept. A previous `config: false` opt-out is flipped to `config: true`, since the hand-written file is being removed. When the config references imports, plugins or any non-literal expression it is reported as **unmapped** and nothing is written, rather than emitting broken output. A JSON `dbx.config.json` is also left alone (the object is folded by hand). When the project has a `vite.config.*` but **no** `dbx.config.*` (a workspace that never had one), the migration creates a fresh `dbx.config.ts` holding the folded `toolchain.vite`, wrapped in `defineConfig` from `@deltabox/toolchain` (like the scaffold). A workspace resolves that import through the root's installed dependency — in a self-hosted repo, via a root `portal:.` self-link. The now-redundant `vite.config.*` is removed and recreated by `dbx sync` as a shim that imports `defineConfig` from the project's own Vite package (`vite` or `vite-plus`, detected from `package.json`). Pass `--ignore-vite-config-file` to keep the original file. ### Nested configs In a monorepo, pass `--recursive` to fold **each workspace's** own `vite.config.*` into that workspace's `dbx.config.*` (creating one when it is missing) instead of the root's: ```sh dbx migrate --from vite --recursive --write ``` Without `--recursive`, only the root's `vite.config.*` is migrated and nested ones are left alone. Detection is per project root (a directory with a `package.json` or `dbx.config.*`, up to five levels deep), so a `vite.config.*` in a deeper subdirectory of a project is not discovered. A workspace with no `dbx.config.*` gets a fresh one holding just its `toolchain.vite`; it inherits the rest of the config from the nearest ancestor at load time. ## Post-migration sync A `--write` run finishes by regenerating the project's toolchain-managed files (the same files `dbx sync` writes) for every project it changed — so a migrated project is immediately consistent, eg. the managed `vite.config.ts` shim appears right after folding a hand-written config. Two flags narrow that step: | Flag | Effect | | ------------------------ | ------------------------------------------------------------------------- | | `--ignore-managed-files` | Skip the post-migration sync entirely; managed files are left untouched. | | `--ignore-changelog` | Run the sync but drop the `changelog` task (workflows, commitlint, hook). | The sync only runs after `--write`; a dry-run reports what it would do instead. ## Package scripts `dbx migrate` migrates the tooling **files**; the matching `package.json` scripts are a small follow-up. The common swaps: | Before | After | | ------------------------ | --------------------------- | | `biome format --write .` | `dbx fmt` | | `biome check --fix .` | `dbx lint --fix` | | `turbo build` | `dbx run -r build` | | `turbo typecheck` | `dbx run -r typecheck` | | `turbo test` | `dbx run -r test` | | `turbo dev` | `dbx run -r --parallel dev` | Then drop `@biomejs/biome` + `turbo` (and `biome.json` + `turbo.json`) and add `@deltabox/toolchain` + `vite-plus`. The full walkthrough — recommended scripts, the dependency diff and per-workspace tasks — is on the [Package scripts](/guide/scripts) page. ## CI check `--check` runs the migrations without writing and exits non-zero when a target file is missing or differs from what the migration would produce, so a pipeline can require that a repo is fully on Oxc: ```sh dbx migrate --check || echo "still on Biome/ESLint — run migrate --write" ``` `--online` allows network access: the ESLint migration shells out to `@oxlint/migrate` to evaluate JS/flat configs (it writes `.oxlintrc.json`, so it runs only together with `--write`). ## Honest output Anything that cannot be mapped faithfully is reported instead of dropped: ``` unmapped (7): - organizeImports !lucide*: negated group members are approximated - linter.rules.suspicious.noUnknownAtRules: no Oxlint equivalent ``` ## Programmatic ```ts import { listMigrations, detectMigrations, runMigration } from '@deltabox/toolchain/migrate'; const detected = detectMigrations(process.cwd()); const result = await runMigration('biome', { cwd: process.cwd(), target: 'vite', write: true }); console.log(result.unmapped); ``` ## Notes `oxfmt --migrate biome`-style tools cover formatter options only; the pieces the upstream tools skip (Biome `organizeImports`, Biome linter rules, Dprint) are handled here. ## Upgrading from v1 See the [v1 → v2 migration guide](/guide/migrations/v1-v2). --- --- url: https://toolchain.deltabox.studio/guide/migrations/v1-v2.md --- # Migrating from v1 to v2 v2 focuses on a smaller, schema-driven config-generator API. Most runtime features keep working, but the programmatic config-generator surface is a breaking change. ## Breaking changes ### `ConfigScopes` and `ConfigScopeDef` are gone Pass a `ConfigSchema` directly. It accepts a Zod schema, an untyped `Schema`, an array of schemas, or a record keyed by scope. ```ts // v1 const engine = createConfigEngine({ scopes: { server: { zodSchema: serverConfig }, cli: { zodSchema: cliConfig }, }, }); // v2 const engine = createConfigEngine({ schema: { server: serverConfig, cli: cliConfig }, }); ``` `ConfigSchemaType` is the untyped `Schema` plus the toolchain's `ConfigMeta`. ### Render functions are internal Use the `generateConfig*` functions or the engine methods. They return generated contents and only write when you pass an output directory/path. ```ts // v1 renderConfigTypes(scopes, { outputDir }); renderScopeMarkdown(scopes, { outDir }); // v2 generateConfigTypes(schema, { outputDir }); generateConfigMarkdown(schema, { outputDir }); ``` Set `outputDir: false` (or omit it) to generate without writing. ### Schema maps were removed `CONFIG_SECTION_SCHEMAS`, `CONFIG_TYPE_SCHEMAS` and `CONFIG_SECTION_DESCRIPTIONS` were removed. Use `configSchema` (the section schemas) or `dbxConfigSchema`, and read descriptions from each schema's `.describe()` / `.meta()`. ### Metadata is the source of truth Generators read `ConfigMeta` from Zod `.meta()` or from untyped schema nodes. `env`/`path` are deprecated in favour of `envName`/`envPath`; `cliFlag` and `cliFlagShort` customize CLI flags. Types can use `typeName` / `id` to control the generated interface name. ### Collection names * `collectEnvVars` → `collectVars` * `collectRequiredEnvVars` → `collectRequired` * `EnvVarDef` → `ConfigDef` * `EnvValueType` → `ConfigValueType` * engine `requiredEnvVars` → `requiredVars` * `writeMarkdown({ outDir })` → `writeMarkdown({ outputDir })` ## Behaviour changes * `toolchain.fmt` / `toolchain.lint` shorthand (`true` or `{ vscode: ... }`) now infers a tool from installed packages, then VS Code user settings, then falls back to Oxc defaults. Existing user config files are folded through the migration mappings instead of being overwritten. * Formatter/linter conflict warnings use `fmt.conflict` / `lint.conflict`. Missing tools warn with `fmt.no-tool` / `lint.no-tool`. * `fmt.vscode` / `lint.vscode` only add `editor.defaultFormatter`, language blocks and `editor.codeActionsOnSave`; use `{ overrides, exclude }` to adjust them. * A custom logger can be supplied through `toolchain.logger.logger`. * Changelog versioning supports custom bump rules/callbacks, over-bump opt-in, provider selection and commit credit/format options. ## tsconfig changes ### Resolve paths/references programmatically or from the CLI v2 adds a extends-aware resolver. Use it instead of hand-rolling alias parsing: ```ts import { resolveTsconfig } from '@deltabox/toolchain/tsconfig'; const { alias, paths, references } = resolveTsconfig({ root: process.cwd(), tsconfig: 'tsconfig.json', // optional; defaults to /tsconfig.json }); ``` ```sh dbx tsconfig --json # { alias, paths, references } dbx tsconfig --alias # Vite/Vitest resolve.alias entries dbx tsconfig --paths # resolved compilerOptions.paths dbx tsconfig --references # resolved project references dbx tsconfig --tsconfig tsconfig.build.json dbx tsconfig --root packages/app --no-extends ``` The resolver follows `extends` per field: a child can define `paths` while inheriting `references` (or the reverse). When a config has no `baseUrl`, its relative paths resolve from the config file that declares them. ### Stop editing generated `tsconfig.json` files `dbx` owns the generated `tsconfig.json`; move customizations to `tsconfig.default.json` or `toolchain.tsconfig.overrides` and re-run sync: ```ts export default defineConfig({ toolchain: { tsconfig: { overrides: { base: { compilerOptions: { target: 'ES2022' } }, default: { compilerOptions: { strict: true } }, build: { compilerOptions: { declaration: true } }, root: { compilerOptions: { types: ['node'] } }, }, }, }, }); ``` ```sh dbx sync --recursive dbx sync --recursive --check ``` Overrides are deep-merged into the user-owned files (`baseFile`, `rootFile`, `defaultFile`, `buildFile`; custom names are honoured). The first time a file is patched, its original content is snapshotted in `.dbx/toolchain/manifest.json` under `snapshots` (a tree of **relative** paths mirroring the repo), and the applied override under `tsconfigOverrides`. Deleting an override key reverts it: the original value comes back when the snapshot defines the key, otherwise the key is removed (a value edited after the override set it is kept). A missing target is skipped with a `tsconfig.missing-file` warning instead of being created. ```sh dbx tsconfig --restore # remove every applied override now dbx tsconfig --restore build # only base | root | default | build ``` `dbx reset` restores the overrides before it removes the manifest. The patched files are never part of the manifest's managed `files`, so neither a sync nor a reset deletes them. See [tsconfig → `overrides`](/guide/tsconfig#overrides). `tsconfig.baseFile` (default `tsconfig.base.json`) names the root base tsconfig that `overrides.base` targets. Use `tsconfig.extends` to choose what scaffolded `tsconfig.base/default/build.json` files extend (for example `build: './tsconfig.json'` to reuse path aliases in builds, at the cost of pulling workspace source into the build). ### Sync task API Custom sync tasks can use these additions: * `SyncTask.watch` / `SyncTask.keep` accept a function of the config (`config => string[]`), so globs can follow configured file names. * A `ManagedFile` returned with `track: false` is written and drift-checked like any other, but stays user-owned: it is not recorded in the manifest, never pruned by a later sync and never removed by `dbx reset`. * `resolveWatchGlobs(tasks, config, projectType?)` (core) resolves the active tasks' inputs; `dbx watch --list [--json]` prints them, and the VS Code extension watches them (custom tsconfig names included) unless `deltabox.toolchain.watch` is set explicitly. Its default is now `[]`. * `SyncContext.manifest` holds task metadata persisted in the manifest between syncs (`snapshots`, `tsconfigOverrides`); `SyncContext.check` is true for `dbx sync --check`. ### Do not hand-add workspace aliases Keep `paths`/`references` in `tsconfig.workspaces.json`; the generator expands them into each workspace. For Vite/Vitest, prefer Vite's native `resolve.tsconfigPaths: true`, or pass `resolveTsconfig().alias` to `resolve.alias`. If you created a `vitest.aliases.ts` during the transition, delete it and use one of those two options. ## Vite and task changes * Runtime shims are still the default (`vite.shim: 'runtime'`). Static shims (`vite.shim: 'static'`) serialize the config and import only `defineConfig`, useful when loading the shim without a built package. * `vite.shimImport` changes the `resolveVite` import specifier for runtime shims. Relative values are resolved from the repo root and rewritten per generated config. * `typecheck:ci` was removed; `typecheck` already uses the source tsconfig. Use `yarn typecheck` in CI and delete any `typecheck:ci` scripts or Vite+ tasks. * The emulate test suite can be cleaned with `yarn test:clean`. ## Recommended upgrade steps 1. Replace `scopes` with `schema` in `createConfigEngine`. 2. Replace `render*` imports with `generateConfig*`. 3. Remove imports of the deleted schema maps; use `configSchema` or individual schema exports. 4. Rename `env`/`path` metadata to `envName`/`envPath` when convenient (the old names still work). 5. Remove hand-written `tsconfig.json` aliases and move them to `tsconfig.workspaces.json`; run `dbx sync --recursive`. 6. Replace any custom tsconfig path parser with `resolveTsconfig()` (or `dbx tsconfig --json`). 7. Delete `vitest.aliases.ts` if present; enable `resolve.tsconfigPaths: true` or use `resolveTsconfig().alias`. 8. Replace `typecheck:ci` with `typecheck` in scripts/CI. 9. Optionally move options you want managed in `tsconfig.base.json` / `tsconfig.workspaces.json` / the workspace default and build tsconfigs into `toolchain.tsconfig.overrides`, and keep committing `.dbx/toolchain/manifest.json` (it now holds their snapshots). 10. Run `yarn dbx sync`, `yarn config:types`, `yarn typecheck`, `yarn test` and `yarn dbx sync --recursive --check` after upgrading. --- --- url: https://toolchain.deltabox.studio/guide/project.md --- # Project The `project` section is a package's **canonical metadata**. It is the single place to declare who a package is, and it does three jobs: 1. **Defines `package.json` fields.** On `dbx sync`, the fields the section defines are written into the project's `package.json` (everything else is left untouched). 2. **Feeds the `{{ token }}` replacements.** Every template (`biome.json`, workflows, tsconfigs, READMEs, ...) reads these values. 3. **Drives scaffolding.** `dbx project:init*` uses the section as its defaults (and `project.type` picks the kind of project to generate). ```ts [dbx.config.ts] export default defineConfig({ toolchain: { project: { type: 'monorepo-root', name: 'acme', scope: '@acme', author: 'Acme Inc. ', license: 'MIT', homepage: 'https://acme.dev', repo: 'https://github.com/acme/acme', description: 'The acme monorepo', nodeVersion: '22', packageManager: 'yarn', packageManagerVersion: '4.17.1', }, }, }); ``` ## `package.json` mapping A sync writes only the fields the config **defines**; a field you omit is never touched. The mapping: | `project` field | `package.json` field | Notes | | ---------------- | -------------------- | -------------------------------------------------- | | `name` + `scope` | `name` | `@acme/app` when a scope is set, else `app`. | | `description` | `description` | | | `author` | `author` | | | `license` | `license` | | | `homepage` | `homepage` | | | `repo` | `repository` | `{ type: 'git', url }` (+ `directory`, see below). | | `packageManager` | `packageManager` | `yarn@4.17.1` when `packageManagerVersion` is set. | | `type` | — | Project kind for detection/scaffolding. | | `nodeVersion` | — | Node version for CI/templates (not `engines`). | `nodeVersion` is intentionally **not** written to `engines`: it is the Node version the generated workflows run on (`node-version: '{{nodeVersion}}'`), which is a different concern from the published `engines.node` range. ## Workspace inheritance A non-root `dbx.config.*` is merged over the nearest ancestor config (see [Configuration → Nested configs](/guide/config/nested-configs)). For `project` specifically: * **Identity fields are never inherited**: `name`, `scope`, `description` and `type` are per-project. A workspace keeps its own values, falling back to its `package.json` for `name`/`scope`/`description`, and is still detected from its own files — a root `project` never renames or rescopes a workspace. The scope therefore comes from the workspace's own `package.json` name (eg. `@acme/web`); an unscoped name stays unscoped unless the workspace config sets `scope`. * **Shared metadata is inherited** (`author`, `license`, `homepage`, `repo`, `packageManager`, ...), so those `package.json` values and replacement tokens stay shared. * **`inherit: false`** opts out entirely: the project keeps only its own `project` and ignores the parent's (useful for an unrelated package inside the tree). ```ts [packages/web/dbx.config.ts] export default defineConfig({ toolchain: { // `name`/`scope` are identity; here the scope comes from the package's // `@acme/web` name. `author`/`license`/`repo` still inherit from the root. project: { name: 'web' }, // ... }, }); ``` ### `repo` resolves to the workspace path For a non-root project, `repo` also resolves to the workspace's location in the repository, written as `repository.directory`: ```json [package.json] { "repository": { "type": "git", "url": "https://github.com/acme/acme", "directory": "packages/web" } } ``` The directory is computed relative to the repository root (the nearest ancestor with a `.git`), exactly like npm expects for a monorepo workspace. ## When the section is absent If `project` is not defined, nothing is written to `package.json` — and the replacement tokens fall back to the project's **own `package.json`** values (`name`/`scope` from `name`, plus `description`, `author`, `license`, `homepage`, `repository`, `engines.node`, `packageManager`). So a repo with no `project` section still generates correct tokens from its existing metadata. ## Scaffolding `dbx project:init`, `dbx project:init:monorepo` and `dbx project:init:workspace` read `project` as their defaults, and: * **`name` is required** (from `--name` or `project.name`) — a scaffold fails with a clear error when it is missing. A workspace always needs an explicit `--name` (the root's name is not the workspace name). * **`--type` must be valid** (`project`, `monorepo-root` or `monorepo-workspace`) when passed explicitly; an invalid value is rejected. A `project.type` in the config still drives `dbx sync` detection, but scaffolding deliberately ignores it (a scaffold launched from a monorepo root must not tag a workspace with the root's type). ```sh dbx project:init --name my-app --scope @acme --type project --out ./my-app dbx project:init:monorepo --name acme --scope @acme --out . dbx project:init:monorepo --name acme --scope @acme --example --out . dbx project:init:workspace --name web --root . --dir packages/web ``` The scaffolded `dbx.config.ts` spells the resolved `project` fields out, so a generated project is self-describing from day one. Existing files in the output directory are never overwritten (pass `--force` to replace them), and a scaffold refuses to write outside its output directory. A nested `project:init` below a root that already has `.oxfmtrc.json` / `.oxlintrc.json` skips its own Oxc files and `fmt.oxc` / `lint.oxc` opt-ins, so it uses the root formatting/linting setup. Pass `--package-manager ` (`yarn` default, or `pnpm` / `npm` / `bun`) to choose the manager the scaffold targets. ## Package manager and catalogs The scaffold and `dbx sync` write a **catalog** (`package -> version range`) into the file the package manager reads it from, so dependencies can be declared with `catalog:`: | Manager | Catalog file | Notes | | ------- | --------------------- | -------------------------------------------- | | `yarn` | `.yarnrc.yml` | `catalog:` / `catalogs:` (default + named). | | `pnpm` | `pnpm-workspace.yaml` | `catalog:` / `catalogs:`. | | `bun` | `package.json` | `workspaces.catalog`. | | `npm` | — | No catalog support; dependencies are pinned. | Scaffolding resolves the manager from the `packageManager` input, writes the catalog of the packages the project depends on, and **opts into it in the generated `dbx.config.*`**, so `dbx sync` owns the file. To manage catalogs in an existing project, add a `catalogs` section (see [Package catalogs](/guide/catalogs)): ```ts [dbx.config.ts] export default defineConfig({ toolchain: { project: { packageManager: 'yarn' }, catalogs: { packages: { react: '^19.0.0', lodash: '^4.17.21' }, named: { legacy: { react: '^18.0.0' } }, }, }, }); ``` `dbx sync` writes (and keeps in sync) `catalog:` / `catalogs:` in `.yarnrc.yml` (Yarn) or `pnpm-workspace.yaml` (pnpm). A catalog file is only written when it is absent or already toolchain-managed (it carries a marker comment); a hand-written one is left untouched. Bun catalogs live in `package.json` and npm has no catalog support — both emit a warning. ## Programmatic API ```ts import { projectFromPackageJson, projectToPackageJson, projectSyncTask } from '@deltabox/toolchain'; // Seed a config from an existing package.json. const project = projectFromPackageJson(require('./package.json')); // Map the defined fields onto package.json fields. const pkgFields = projectToPackageJson(project, { directory: 'packages/web' }); // The sync task itself. projectSyncTask(); ``` | Export | Description | | ----------------------------- | ------------------------------------------------------------------- | | `projectFromPackageJson(pkg)` | Derive project metadata from a `package.json`. | | `projectToPackageJson(p, o?)` | Map defined project fields onto `package.json` fields. | | `projectSyncTask()` | The sync task that applies the `project` section to `package.json`. | --- --- url: https://toolchain.deltabox.studio/guide/providers.md --- # Providers A **provider** is an external toolchain exposed through `dbx`: its CLI, the programmatic API and the VS Code extension. Providers contribute namespaced commands and a descriptor the editor reads over `dbx providers --json`. ## List providers ```sh dbx providers # table dbx providers --json # descriptor (id, binary, commands, actions) ``` ## Vite+ (`vite-plus`) The bundled provider wraps the `vp` binary — the unified Vite / Vitest / Oxlint / Oxfmt / Rolldown / tsdown toolchain. ### Resolution `dbx vite:*` resolves `vp` in this order: 1. the project-local shim (`node_modules/.bin/vp`), 2. a global `vp` on `PATH`, 3. `npx --yes vite-plus`. ### Generic passthrough ```sh dbx vite dev dbx vite build dbx vite add zod dbx vite check ``` Everything after `vite` is forwarded verbatim to `vp`, so all Vite+ commands and flags are reachable. A bare `dbx ` whose name is a Vite+ command (or a Vite+ alias) is forwarded too, so `dbx fmt` / `dbx run -r build` need no `vite` namespace — built-in commands win (`dbx migrate` stays the toolchain's). ### Colour Provider output keeps `vp`'s own colours, but follows the logger's colour state. The global `--no-color` flag turns provider colour off, and the `providers` section can opt out on its own even on a colour terminal: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { providers: { color: false }, }, }); ``` When colour is off, `dbx` runs `vp` with `NO_COLOR=1` and `FORCE_COLOR=0` so the wrapped process emits plain output. Leave the section unset to let `vp` detect colour from its own terminal. ### Named commands Every catalogued subcommand is also available as a first-class command for discovery + the editor: ``` vite:create vite:migrate vite:dev vite:check vite:lint vite:fmt vite:test vite:build vite:pack vite:run vite:add vite:remove vite:update ... vite:toolchain vite:upgrade vite:implode ``` ### Programmatic ```ts import { resolveVp, runVp, hasVp, vpVersion, describeVp, resolveVpCommand, viteCommandName, VP_COMMANDS, } from '@deltabox/toolchain/vite'; if (hasVp(process.cwd())) { await runVp(['check'], { cwd: process.cwd() }); } ``` | Export | Description | | -------------------------------------- | --------------------------------------------------------------------------- | | `resolveVp(cwd?)` | Resolve the `vp` invocation (`{ exe, args, source }`). | | `hasVp(cwd?)` | Whether a local/global `vp` is available (no `npx` fallback). | | `runVp(args, options?)` | Run `vp`, streaming output or `mode: 'capture'`; never exits the host. | | `vpVersion(cwd?)` / `describeVp(cwd?)` | The `vp --version` output / a one-line resolution description. | | `which(bin)` | Find an executable on `PATH` (resolves `PATHEXT` on Windows). | | `resolveVpCommand(name)` | Resolve a subcommand **or alias** to its canonical name (`i` -> `install`). | | `viteCommandName(name)` | The `dbx` command name for a `vp` subcommand (`dev` -> `vite:dev`). | | `VP_COMMANDS` | The `vp` command catalog that drives the CLI + the provider descriptor. | ```ts // A bare `dbx ` falls through to `vite` when it names a Vite+ command. resolveVpCommand('fmt'); // 'fmt' viteCommandName('fmt'); // 'vite:fmt' ``` ### Use dbx from Vite+ Vite+ consumers can drive dbx from `vite.config.ts`, so a Vite+-only project still gets dbx's generated/managed files: ```ts [vite.config.ts] import { defineConfig } from 'vite-plus'; import { dbx, DBX_TASKS } from '@deltabox/toolchain/vite'; export default defineConfig({ plugins: [dbx()], run: { tasks: { ...DBX_TASKS } }, // your usual Vite / Vite+ config }); ``` * The `dbx()` plugin runs `dbx sync` before the dev server / build, or `dbx sync --check` in CI (which fails the build when generated files are out of date), and re-syncs when a `dbx.config.*` / `tsconfig.*.json` input changes. * The plugin resolves the `dbx` binary from the project first: the local `node_modules/.bin/dbx`, then the project's own `@deltabox/toolchain` bin. A global `dbx` on `PATH` is used **only when the project doesn't pin the toolchain at all** — otherwise it's skipped (with a warning) in favour of `npx @deltabox/toolchain@`. That way a stale global install can never run and silently revert the project's generated files. * `run: { tasks: { ...DBX_TASKS } }` also exposes `vp run dbx:sync` / `vp run dbx:check`. Vite+ reads `run.tasks` from the **static** config (a plugin's `config` hook is ignored), so the tasks are spread in the config rather than added by the plugin. Drop the `run` block if you don't want them. * Because Vite Task runs a task's `command` through a shell, `DBX_TASKS` bakes in the binary resolved by `resolveDbx` (the project's own, or the pinned `npx` fallback) rather than relying on `dbx` being on `PATH` — a missing environment then warns instead of silently running the wrong binary. Use `dbxTaskCommand(sub)` to build a task command for another subcommand. ## Custom providers Providers are loaded from **search paths** (JSON files, directories of `*.json`, or `http(s)` registry URLs). A JSON file can be an array of provider descriptors or `{ "providers": [...] }`. Project paths (first wins on an id clash): * `.dbx/toolchain/.registry/providers` — the implicit default directory; * `.dbx/toolchain/registries.json` — its `providers` array; * `.dbx/toolchain/providers.json` — the legacy file, still read. Global paths: * `~/.dbx/toolchain/.registry/providers` — the default directory; * `~/.dbx/toolchain/cli/config.json` — `providerRegistrySearchPaths`, or `registrySearchPaths.providers`; * the VS Code `deltabox.providerRegistrySearchPaths` setting (kept in sync). `toolchainProviders()` merges every local registry with the built-ins (a project provider wins on an id clash), so a custom provider's commands become `dbx` commands — `dbx ` forwards to ` ...` — and it shows up in `dbx providers` and the editor. Remote registries are fetched by `dbx providers --json`/`loadToolchainProviders()`. Restrict them with `toolchain.registry.providers` (`allow`/`deny` host + path globs); the same policy applies to local registry paths: ```json [.dbx/toolchain/.registry/providers/providers.json] [ { "id": "echo", "name": "Echo", "binary": "node", "commands": [ { "name": "echo:version", "subcommand": "--version", "description": "Print the Node version" } ] } ] ``` ```sh dbx registry --json # list template + provider paths dbx registry --kind providers --scope project --add ./providers dbx registry --kind providers --scope global --add https://example.com/providers.json ``` The VS Code **Providers** view manages the registry items (**Add provider...** / **Remove provider**), and the **Registries** view shows the provider search paths. See [VS Code Extension](/guide/vscode). ## Providers in the editor The VS Code extension reads `providers --json` and builds its **Providers** view and menus from it, so adding a provider surfaces it automatically. See [VS Code Extension](/guide/vscode). --- --- url: https://toolchain.deltabox.studio/guide/scripts.md --- # Package scripts `dbx` writes files, not your `package.json` — but a few scripts are worth standardizing, because the toolchain's formatting/linting and task running both go through **Vite+**. Any bare `dbx ` that matches a Vite+ command is forwarded straight to it (`dbx fmt`, `dbx run -r build`, `dbx dev`), so a project only needs the one `dbx` command. This page shows the recommended root scripts and how to move an existing Biome + Turbo project onto them. ## Recommended scripts The repo root `package.json`: ```json [package.json] { "scripts": { "format": "dbx fmt", "lint": "dbx lint --fix", "check": "dbx check", "build": "dbx run -r build", "typecheck": "dbx run -r typecheck", "test": "dbx run -r test", "dbx:sync": "dbx sync", "dbx:watch": "dbx watch", "commitlint": "commitlint", "postinstall": "husky" } } ``` | Script | What it does | | ------------------------------ | --------------------------------------------------------------- | | `format` / `lint` / `check` | Oxfmt / Oxlint across the repo (`check` = format check + lint). | | `build` / `typecheck` / `test` | Runs the task in every workspace, in dependency order. | | `dbx:sync` / `dbx:watch` | Regenerate the managed files / re-sync on change. | | `commitlint` / `postinstall` | Conventional Commits via commitlint + husky. | Notes: * A bare command that names a Vite+ command is forwarded to Vite+ (`vp`) — eg. `dbx fmt`, `dbx dev`, `dbx run -r build`, `dbx add zod` (Vite+ aliases too). Built-in commands win, so `dbx migrate` is the toolchain's migration — use `dbx vite migrate` for Vite+'s. The explicit `dbx vite ` passthrough always works. * Vite+ resolves from the **repo root**, so `format`/`lint` live there — keep them out of the individual workspaces. * `dbx run -r ` runs a task in every workspace package in dependency order. A task is either a `package.json` script or a `vite.config.ts` `run.tasks` entry. For long-running processes use `dbx run -r --parallel dev`. * `dbx` is the toolchain binary, so `yarn dbx ` (eg. `yarn dbx sync`) works even without a script; the `dbx:*` aliases are just for discoverability. When `ci.changelog` is enabled, `dbx sync` also writes a husky `commit-msg` hook (`.husky/commit-msg`) that runs commitlint, so commits are linted locally as well as in CI. Use `postinstall` (not `prepare`) to activate husky: Yarn Berry never runs `prepare` on install. Husky hooks have no file extension, so on Windows add `.husky/* text eol=lf` to `.gitattributes` — a trailing CR silently breaks the hook. ## Migrate an existing project 1. Add the toolchain + Vite+, and drop what they replace: ```diff - "@biomejs/biome": "catalog:", - "turbo": "^2.11.2", + "@deltabox/toolchain": "^1.2.3", + "vite-plus": "^1.2.0", ``` 2. Fold the Biome config into a root `vite.config.ts`: ```sh dbx migrate --from biome --target vite --write ``` Or, if the project uses `dbx.config.ts`, author the Vite+ config there under `toolchain.vite`. `dbx sync` then writes the root `vite.config.ts` shim (see [Vite+](/guide/config/vite)). `dbx migrate --from vite --write` folds an existing hand-written `vite.config.ts` in. 3. Replace the scripts (see the tables below), then delete `biome.json` and `turbo.json`. 4. Add a `dbx.config.ts` and sync the managed files: ```sh dbx sync ``` 5. Verify — this is what CI runs: ```sh dbx sync --check yarn build ``` ### Biome → Oxc | Before | After | | ------------------------ | ---------------- | | `biome format --write .` | `dbx fmt` | | `biome check --fix .` | `dbx lint --fix` | | `biome.json` | `vite.config.ts` | Remove `@biomejs/biome`, delete `biome.json`, and drop any per-workspace `format`/`lint` scripts. ### Turbo → Vite Task | Before | After | | ----------------- | ------------------------------------------ | | `turbo build` | `dbx run -r build` | | `turbo typecheck` | `dbx run -r typecheck` | | `turbo test` | `dbx run -r test` | | `turbo dev` | `dbx run -r --parallel dev` | | `turbo.json` | (optional) `run.tasks` in `vite.config.ts` | Remove `turbo` and `turbo.json`. Ordering comes from each package's `package.json` dependencies, so no `turbo.json` graph is needed. ## Per-workspace tasks For cached, dependency-aware tasks, define them in each package's `dbx.config.*` rather than `package.json` — a task and a script cannot share a name: ```ts [packages//dbx.config.ts] import { defineConfig } from '@deltabox/toolchain'; export default defineConfig({ toolchain: { vite: { run: { tasks: { build: { command: 'tsdown --config-loader unrun', cache: { input: [{ auto: true }, '!dist/**'], output: ['dist/**'] }, }, test: { command: 'vitest run', cache: { input: [{ auto: true }], output: [] }, }, }, }, }, }, }); ``` See the [Monorepo guide](/guide/monorepo) for the full task setup, [Migrations](/guide/migrations/) for the `dbx migrate` reference, and [Providers (Vite+)](/guide/providers) for the `dbx vite` passthrough. --- --- url: https://toolchain.deltabox.studio/guide/templates.md --- # Templates Templates scaffold projects, workspaces, applications, folders, tooling or pipelines. They can be **offline** (builtin, or a local `template.json` directory) or **online** (npm `@scope/create` manifests, git repos, and Vite+ templates). ## Kinds Every template declares a `kind`, used by `template:list --kind` and `create --kind`: | Kind | Produces | | ------------- | -------------------------------------- | | `project` | A single-package project. | | `workspace` | A package inside an existing monorepo. | | `application` | A runnable application package. | | `folder` | A bare folder of files. | | `tooling` | Toolchain config/pipeline files. | | `pipeline` | A CI/pipeline template. | ## List + create ```sh dbx template:list dbx template:list --kind application # filter by kind dbx template:list --online # also resolve npm/git sources dbx template:list --registry # only the .dbx/toolchain sources dbx template:list --json # machine-readable (used by the editor) dbx create --template project --name my-app --out ./my-app dbx create --kind folder --name scratch --out ./scratch dbx create --template mytpl --name acme --var port=4000 ``` With no `--template`, `create --kind ` picks the first matching template. An unlisted `--template` id is treated as a Vite+ template (`vp create `). On an interactive terminal, a bare `dbx create` (no `--template`/`--kind`/`--name`) prompts for the template, name and scope instead (Ctrl-C cancels). ## Builtin templates | id | Kind | Description | | ----------------------- | ------------- | ---------------------------------------------- | | `project` | `project` | A single-package TypeScript project. | | `application` | `application` | A runnable application package. | | `folder` | `folder` | A bare folder (a README). | | `monorepo` | `project` | A monorepo root (no example workspace). | | `monorepo-example` | `project` | A monorepo root + one example workspace. | | `workspace` | `workspace` | A workspace package into an existing monorepo. | | `vite-plus` | `project` | `vp create` (interactive or templated). | | `vite-plus-monorepo` | `project` | `vp create vite:monorepo`. | | `vite-plus-application` | `application` | `vp create vite:application`. | | `vite-plus-library` | `project` | `vp create vite:library`. | | `vite-plus-generator` | `project` | `vp create vite:generator` (monorepo only). | The offline builtins (`project`, `application`, `folder`, `monorepo`, `monorepo-example`, `workspace`) scaffold a generic, current project: a `dbx.config.ts` with `project` metadata, a runnable `src/index.ts` + smoke test, and a `package.json` that depends on the same `@deltabox/toolchain` version as the CLI. There is no Turbo (Vite Task replaces it). They ask for a **package manager** (default `yarn`) and write that manager's catalog file (see [Package catalogs](/guide/catalogs)). The `vite-plus*` builtins delegate to `vp create` and need network access. The root-oriented builtins (`project`, `application`, `monorepo`, `monorepo-example`) also write `.oxfmtrc.json` / `.oxlintrc.json` and opt in with `fmt.oxc` / `lint.oxc`. A nested `project`/`application` scaffold instead detects an ancestor that already owns those files and omits both the local files and the config opt-ins, so it inherits the root formatting/linting. `folder` and `workspace` never create Oxc files. Existing files are never overwritten unless `dbx create --force` is passed. ## Inputs (variables) A template can declare **inputs** — values prompted for (and substituted) when it is scaffolded. Declare them in `template.json`: ```json [template.json] { "variables": [ { "name": "name", "prompt": "Project name", "default": "app", "required": true }, { "name": "port", "prompt": "Dev port", "default": "4000" }, { "name": "includeDocker", "prompt": "Include a Dockerfile?", "options": ["yes", "no"] } ] } ``` * `name` — the token name (also `{{ name }}` in files). * `prompt` — the prompt text (defaults to the name). * `default` — the value used when nothing is entered. * `options` — a fixed list of choices: the value is picked from a select. * `required` — whether a value must be provided. Files and directory names may contain `{{ token }}` placeholders, resolved while copying: ``` src/{{name}}.ts -> src/acme.ts ``` The **same input pipeline** runs everywhere: * **CLI** — `dbx create` prompts for each input on an interactive terminal (the standard `name`/`scope` first, then the template's own inputs). Pass `--var key=value` (repeatable) to skip a prompt. * **Programmatic API** — `scaffoldTemplate(template, { cwd, tokens, variables })`. * **VS Code** — **Create from template…** prompts for each input (a quick pick when `options` is set). The builtin templates use the same pipeline (they declare `name`, `scope`, `packageManager` and `includeConfig` inputs). ## Actions After the files are written, a template can run **actions** — extra steps such as installing dependencies. Declare them in `template.json`: ```json [template.json] { "actions": [ { "id": "install", "prompt": "Install dependencies?", "builtin": "install" }, { "id": "setup", "prompt": "Run the setup script?", "module": "action.mjs" }, { "id": "post", "command": "node scripts/post-scaffold.mjs" } ] } ``` * `id` — the action id (also the `--action ` key). * `prompt` — the prompt text (defaults to `Run ?`). * `default` — whether it runs by default (**default false** — opt-in). * `builtin` — a built-in action. `install` runs ` install` in the output directory. * `module` — a JS module (relative to the template directory) whose default export is `async (ctx) => void`. The module is **not** copied to the output. The context is `{ cwd, templateDir, tokens, variables, packageManager, log }`. * `command` — a shell command run in the output directory. Actions run through the same pipeline everywhere: the CLI prompts (or takes `--action `, repeatable), the extension prompts, and the programmatic API passes `actions: string[]` to `scaffoldTemplate`. The builtin templates declare an opt-in **Install dependencies?** action (`builtin: 'install'`, off by default). ## Local templates A local template is a directory with a `template.json`. Turn any folder into one with `template:init` (and register it in one step with `--add`): ```sh dbx template:init ./templates/acme-service \ --kind application --name "Acme service" --var name --tag service --add ``` ```json [template.json] { "name": "Acme service", "description": "A service package", "kind": "application", "tags": ["service"], "variables": [{ "name": "name", "default": "service" }], "ignore": ["fixtures"] } ``` Register sources so they are picked up automatically: ```sh dbx template:add ./templates # local dir (auto-inits if needed) dbx template:add @acme/create # npm @scope/create dbx template:add git:https://github.com/acme/tpl.git dbx template:remove local:./templates # drop a source ``` `template:add` records them in `.dbx/toolchain/templates.json`; `template:remove` takes an id or a `type:locate` key. Sources can also be declared in the config: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { templates: { sources: [ { type: 'local', locate: './templates', kind: 'application' }, { type: 'npm', locate: '@acme/create', online: true }, ], }, }, }); ``` ## Global templates A template registered with `template:init --add` or `template:add` is **also** stored in the global registry at `~/.dbx/toolchain/.registry/templates`, so it is available in every project (and in the VS Code extension). Pass `--no-global` to keep it project-only: ```sh dbx template:init ./templates/acme --kind application --name "Acme" --add # local + global dbx template:add ./templates --no-global # project only ``` Global templates are merged with the project's own; a project template wins on an id clash. The global search paths default to `~/.dbx/toolchain/.registry/templates`, and each path may be a directory or an `http(s)` **remote registry** (a JSON `{ "sources": [...] }` or an array of sources): ```sh dbx template:registry # list the resolved paths dbx template:registry --add ~/shared/templates --add https://acme.dev/templates.json dbx template:registry --set ~/shared/templates dbx template:registry --remove ~/shared/templates dbx template:registry --clear # reset to the default ``` The paths live in the CLI config `~/.dbx/toolchain/cli/config.json` (`registrySearchPaths`, a string or array) and are kept in sync with the VS Code setting `deltabox.registrySearchPaths`: the extension records the VS Code user dir in that config and mirrors setting changes into it, and the CLI writes the setting back when you change the paths. `vscodeDir` in the CLI config points at the VS Code user directory (the one containing `settings.json`). The project-scoped registries are stored in `.dbx/toolchain/registries.json` and default to `.dbx/toolchain/.registry/templates`. `dbx registry [--json]` lists the detected template/provider registries, and `dbx registry --add --scope global|project` (or `--remove`) manages them. A remote (`http(s)`) registry is a JSON `{ "sources": [...] }` / array of sources. The VS Code **Registries** view (below **Templates**) shows the Providers/Templates roots with **New registry…** / **Remove registry…** actions; remote registries open in the browser. `dbx template:remove --global ` removes a stored global template. ## Online templates Online sources are resolved with `--online` (or `dbx template:add`): * **npm** — a published create package whose `package.json` has a `createConfig.templates` (or `dbx.templates`) manifest. Scoped (`@acme/create`) and unscoped (`create-acme`, `acme-create`) packages are supported; individual templates are listed and created through `vp create`. The registry defaults to the project package manager's registry (`.yarnrc.yml` `npmRegistryServer`, `.npmrc` `registry`, ...). Override it per source with `--registry`: ```sh dbx template:add @acme/create dbx template:add create-acme dbx template:add create-acme --registry https://registry.example.com ``` * **git** — a repository (optionally with a `ref` and/or `subdir`) listed as a template and created through `vp create`: ```sh dbx template:add git:https://github.com/acme/templates --ref v1.2.0 --subdir apps/web ``` * **remote** — an `http(s)` JSON source registry (`{ sources }`, `{ templates }` or an array of sources). Its sources are merged into the registry; each is then loaded by its own type: ```sh dbx template:add remote:https://example.com/dbx-templates.json ``` * **Vite+** — set `create.defaultTemplate` in the `dbx.config.*`'s `vite` section (the full Vite+ config) to make a scope the default for `vp create`. Sources and registries can be restricted with host/path globs (checked for local paths and remote URLs, which also guards SSRF): ```ts [dbx.config.ts] toolchain: { registry: { templates: { allow: ['*.example.com', 'https://registry.example.com/**', '**/templates/**'], deny: ['*.internal.example.com'], }, }, } ``` ## VS Code The extension drives the same CLI: **From template…** runs `template:list --json` then `create`; **Add template…** runs `template:init --add` on a folder you pick and asks whether to add it to the **global** registry (yes by default); **Remove template…** runs `template:list --registry` then `template:remove`. The `deltabox.registrySearchPaths` setting controls the global search paths (see [Global templates](#global-templates)). --- --- url: https://toolchain.deltabox.studio/guide/tsconfig.md --- # tsconfig The `tsconfig` section generates every workspace's `tsconfig.json` from a workspace default tsconfig (`tsconfig.default.json`), layering path aliases and project references from a single root workspaces tsconfig (`tsconfig.workspaces.json`). It runs only at a **monorepo root** (`project.type: 'monorepo-root'`); a single project keeps its hand-written tsconfig untouched. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { project: { type: 'monorepo-root', name: 'acme', scope: '@acme' }, tsconfig: { aliases: true, refs: true, indent: 4 }, }, }); ``` `dbx sync` regenerates the managed `tsconfig.json` files and tracks them in `.dbx/toolchain/manifest.json`; `dbx sync --check` is the CI gate. See [Configuration](/guide/config/opt-in-by-presence) for the opt-in-by-presence shorthand (`tsconfig: true` enables the defaults). ## Why so many tsconfig files? TypeScript's language service (IntelliSense, diagnostics, autocomplete, "go to definition", project references, ...) **always** picks up the file literally named `tsconfig.json`. It offers no way to point the editor at a different file, and it only understands the options it can express in a single config. That single-file constraint is painful in practice: * **Builds need different options than the editor.** `tsc` for a build wants `declaration`/`composite`/`emitDeclarationOnly`, a wider `include` and no `noEmit`, while the editor wants fast, no-emit checking of `src`. Cramming both into one `tsconfig.json` makes one of the two slower or wrong. * **A monorepo has many projects with different options.** Each workspace wants its own `include`/`lib`/`types`/`jsx`, but the editor only reads the nearest `tsconfig.json`. * **The language service can get slow.** For TypeScript (especially ≤ 7) the editor loads and type-checks whatever the `tsconfig.json` includes; a config that pulls in every workspace (or the whole build graph) can noticeably slow IntelliSense. Keeping the editor config small and correct matters. * **Hand-managing the graph is tedious.** Path aliases and project references have to stay in sync with the workspace list, and a stale reference silently degrades the editor. The toolchain splits the concerns so each file has one job, and `dbx sync` keeps the generated `tsconfig.json` (the file the LSP actually reads) in sync with a shared workspaces tsconfig that declares the `paths` + `references`. **NOTE:** the `paths` + `references` in `tsconfig.workspaces.json` automatically resolves relative paths in each workspace's generated tsconfig. ## The files The generator reads two hand-authored inputs (`tsconfig.default.json`, `tsconfig.workspaces.json`) and writes one managed output (`tsconfig.json`): | File | Owner | Role | | -------------------------- | ---------- | --------------------------------------------------------------------------------- | | `tsconfig.base.json` | you | Shared compiler options that every workspace config extends. | | `tsconfig.default.json` | you | A workspace's own options — the **input** the generator reads. **Optional.** | | `tsconfig.build.json` | you | Build/emit options (`tsc -p tsconfig.build.json`). **Optional, never rewritten.** | | `tsconfig.workspaces.json` | you (root) | The root `paths` + `references` the generator expands. **Optional.** | | `tsconfig.json` | generated | Extends `tsconfig.default.json` + the workspaces aliases/references. | Only `tsconfig.json` must keep its name — that is the file TypeScript's LSP reads. Everything else is optional and, if you prefer, renameable (see [Renaming the files](#renaming-the-files)): * **No `tsconfig.default.json`**: there is nothing to expand (the task logs a message and, when workspaces are expected, warns — see [Warnings](#warnings)). * **No `tsconfig.build.json`**: builds simply use another config; it is never required or rewritten. * **No `tsconfig.workspaces.json`**: each workspace still gets a `tsconfig.json` that extends its own default, but there are no shared `paths`/`references` to wire. The `{{envTypesFile}}` token in the templates (default `src/env.d.ts`) is resolved when a project is scaffolded, so a generated config's `include` points at the project's env types file. ## Development vs build configs `tsconfig.json` is the **editor/development** config: it extends `tsconfig.default.json` and layers the workspace `paths` aliases on top, so imports such as `@acme/core` resolve directly to `packages/core/src`. That is what gives go-to-definition, inline diagnostics and fast editor feedback against the real workspace source. `tsconfig.build.json` intentionally extends `tsconfig.default.json`, **not** `tsconfig.json`, so it does not get those path aliases. A build (`tsc -p tsconfig.build.json`, `tsdown`, ...) therefore treats sibling workspaces as packages (resolved through `node_modules` / project references) instead of treating their source as part of the current package. Keeping them as packages matters: aliasing them into the build graph would compile/bundle another workspace's source into the output and increase the package size. You can opt into build-time aliases by changing the build config to ```json [tsconfig.build.json] { "extends": "./tsconfig.json", "include": ["src"] } ``` It works, but sibling workspace source now participates in the build graph, so only do it when that is what you want. ## Options | Option | Type | Default | Description | | ------------- | ------------------------------------ | -------------------------- | ------------------------------------------------------------------- | | `enabled` | `boolean` | enabled when defined | Master switch (only meaningful with `project.type: monorepo-root`). | | `aliases` | `boolean` | `true` | Inject `compilerOptions.paths` from the workspaces config. | | `refs` | `boolean` | `false` | Wire project references between workspaces. | | `composite` | `boolean` | `false` | Emit `composite: true` on the generated configs. | | `glob` | `string \| string[]` | `**/tsconfig.default.json` | Glob(s) for the workspace default tsconfig files to expand. | | `exclude` | `string \| string[]` | common ignored paths | Glob(s) excluded from `glob`. | | `gitignore` | `boolean` | `true` | Respect `.gitignore` files (root + nested). | | `defaultFile` | `string` | `tsconfig.default.json` | The workspace default tsconfig file name. | | `buildFile` | `string` | `tsconfig.build.json` | The workspace build tsconfig file name. | | `indent` | `number` | `4` | JSON indentation of the generated files. | | `rootFile` | `string` | `tsconfig.workspaces.json` | The workspaces tsconfig file name. | | `baseFile` | `string` | `tsconfig.base.json` | The root base tsconfig file name (the `overrides.base` target). | | `extends` | `{ base?, default?, build? }` | template defaults | Per-file `extends` overrides for scaffolded tsconfig templates. | | `overrides` | `{ base?, default?, build?, root? }` | none | Managed deep-merge overrides applied to the user-owned tsconfigs. | ### `aliases` When on (the default), each generated `tsconfig.json` gets a `compilerOptions.paths` block derived from the workspaces tsconfig's `paths`. The generator rewrites every alias relative to the workspace it is writing, and **drops** any alias that resolves to the workspace itself (or into its own subtree) so a package never maps its own import back to a self-reference. ### `refs` When on, the workspaces `references` are merged with each workspace's own `references`, deduplicated, rewritten relative to the workspace, and pruned whenever the target config is not `composite` (a reference to a non-composite project is invalid). This is what lets `tsc -b` build the graph in order. ### `composite` When on, the generated configs spread the workspaces tsconfig's `compilerOptions` (including `composite: true`, `declaration`, `emitDeclarationOnly`, ...) and merge its `types`. Leave it off unless you build the graph with `tsc --build`. ### `refsAuto` `refsAuto` derives the workspaces `references` from the project graph. It defaults to `auto`; `refs` must also be enabled and each referenced workspace must be `composite`. References are only followed by `tsc --build` (`tsc -b`); **`tsc -p --noEmit` ignores them**. If your typecheck runs `tsc -p --noEmit` (as this repo's does), keep `refsAuto: false` and maintain the `references` in `tsconfig.workspaces.json` by hand, or switch the check to `tsc -b`. ### `glob` The glob(s) selecting the workspace default tsconfig files to expand. A single string or an array: ```ts tsconfig: { glob: '**/tsconfig.default.json'; } tsconfig: { glob: ['packages/*/tsconfig.default.json', 'apps/*/tsconfig.default.json']; } ``` It defaults to `**/`, so setting `defaultFile` alone is usually enough. ### `exclude` / `gitignore` `exclude` drops paths from `glob`; it adds to the common ignored paths the toolchain already skips (`node_modules`, `.dbx`, `dist`, `build`, `coverage`, `.turbo`, `.git`, `.yarn`, ...). `gitignore` (on by default) additionally honours the repo's `.gitignore` files — **root and nested**, each scoped to its own directory, like git — so an ignored workspace is never scanned: ```ts tsconfig: { glob: '**/tsconfig.default.json', exclude: ['**/legacy/**'], gitignore: true, } ``` Set `gitignore: false` to scan ignored paths anyway. The base list is exported as `DEFAULT_IGNORE_PATTERNS` from `@deltabox/toolchain` and `@deltabox/toolchain/shared`, so you can compose your own excludes from it. ### Renaming the files `defaultFile`, `buildFile`, `rootFile` and `baseFile` rename the files the generator reads and the [`overrides`](#overrides) patch: ```ts tsconfig: { defaultFile: 'tsconfig.src.json', // expanded into tsconfig.json buildFile: 'tsconfig.dist.json', // read, never rewritten by the generator rootFile: 'tsconfig.graph.json', // the workspaces paths + references baseFile: 'config/tsconfig.shared.json', // the shared base (`overrides.base`) } ``` Only `tsconfig.json` is fixed (TypeScript's LSP requirement). `rootFile` and `baseFile` are paths relative to the repo root; `defaultFile` and `buildFile` are file names inside each workspace. The custom names are honoured everywhere the sync task looks: the generator, the overrides, the watched inputs (`dbx watch`) and the files it keeps. Project detection looks for `tsconfig.workspaces.json` / `tsconfig.default.json` by default, but honours `rootFile` / `defaultFile` when they are set in the config. Scaffolding (`dbx project:init*`) always writes the default names. ### `indent` The JSON indentation of every generated file (default `4`). ### `extends` Overrides the `extends` field written into **scaffolded** tsconfig templates (`dbx project:init*`). This is separate from the `tsconfig.workspaces.json` `extends` chain used by the resolver. Set a value to add/replace the field, or `false` to remove it: ```ts tsconfig: { extends: { base: '@tsconfig/node22', // tsconfig.base.json currently has no extends default: './tsconfig.base.json', // keep/customize the normal default chain build: './tsconfig.json', // opt into path aliases during builds }, } ``` Existing projects are user-owned by default: edit their `tsconfig.base.json`, `tsconfig.default.json` and `tsconfig.build.json` directly. `dbx sync` only rewrites the generated `tsconfig.json` unless you opt into `overrides`. ### `overrides` Deep-merges managed options into the **user-owned** tsconfigs during `dbx sync`, so a config can define those files programmatically: ```ts tsconfig: { overrides: { base: { compilerOptions: { target: 'ES2022' } }, root: { compilerOptions: { composite: true, types: ['node'] } }, default: { compilerOptions: { strict: true } }, build: { compilerOptions: { declaration: true } }, }, } ``` | Key | Target | | --------- | ---------------------------------------------------------------- | | `base` | the root base tsconfig (`baseFile`, `tsconfig.base.json`) | | `root` | the workspaces tsconfig (`rootFile`, `tsconfig.workspaces.json`) | | `default` | every workspace's default tsconfig (`defaultFile`) | | `build` | every workspace's build tsconfig (`buildFile`) | Overrides are **deep-merged**: objects merge key by key, while arrays and scalars replace the target value. Keys the override does not mention are left alone. The patched files stay yours: they are not added to the manifest's managed `files`, so a sync never deletes them and `dbx reset` never removes them. #### Snapshots The first time an override touches a file, its original content is saved in `.dbx/toolchain/manifest.json` under a top-level `snapshots` field. The tree mirrors the repository with **relative** paths (the manifest is the tracked file under `.dbx`, so it never holds an absolute path), and the applied override per file is recorded under `tsconfigOverrides`: ```json [.dbx/toolchain/manifest.json] { "files": ["packages/core/tsconfig.json"], "snapshots": { "tsconfig.workspaces.json": { "compilerOptions": { "composite": false } }, "packages": { "core": { "tsconfig.build.json": { "extends": "./tsconfig.default.json" } } } }, "tsconfigOverrides": { "tsconfig.workspaces.json": { "kind": "root", "override": { "compilerOptions": { "composite": true, "types": ["node"] } } } } } ``` #### Removing an override Removing an override (or one of its keys) reverts it on the next sync. For every key the override no longer sets, the sync compares the target file with the snapshot: * the snapshot **defines** the key: the original value is restored (eg. dropping `composite: true` above brings back the original `composite: false`); * the snapshot **does not define** it: the key is removed, along with any parent object the override created that is now empty (eg. `types`); * the key holds **another value** than the override wrote: it was edited after the override applied it, so it is kept and a `tsconfig.override-edited` warning is logged. Once no override targets a file anymore, its snapshot and `tsconfigOverrides` entry are dropped. A file is never deleted, even when nothing else is left in it: other tooling may depend on it. #### Restoring `dbx tsconfig --restore` removes the applied override content right away (with the same snapshot comparison), optionally for one kind only: ```sh dbx tsconfig --restore # every kind dbx tsconfig --restore default # base | root | default | build ``` If the overrides are still configured, the command warns (`tsconfig.restore-reapply`): the next `dbx sync` applies them again, so remove them from `toolchain.tsconfig.overrides` to keep the restored files. `dbx reset` does the same restore before it removes the manifest, so resetting a project never strands override content without its snapshots. #### Notes * A target that does not exist is **not created**: the sync warns (`tsconfig.missing-file`) and skips that file. A target outside the project root warns with `tsconfig.outside-root`. * The user-owned files are read as JSONC but written back as plain JSON, so comments are dropped from a file the first time an override changes it. A file whose content would not change is never rewritten. * `dbx sync --check` reports pending override changes (and reverts) as drift without writing them. ## How generation works For each workspace matched by `glob`, the generator: 1. reads its default tsconfig and, when present, the workspaces tsconfig; 2. writes a sibling `tsconfig.json` that `extends: './'`, layering the workspaces `compilerOptions` (aliases/types) and `references` on top; 3. leaves `tsconfig.build.json` alone — it is a user-owned input, like the default tsconfig. Because the output only ever `extends` the default and adds the workspaces wiring, your workspace-specific options stay in the default tsconfig (the file the generator reads, not the one it writes). ## Warnings | Id | When | | -------------------------- | ------------------------------------------------------------------ | | `tsconfig.missing-file` | An override targets a file that does not exist (it is skipped). | | `tsconfig.outside-root` | An override target resolves outside the project root. | | `tsconfig.override-edited` | A reverted key was edited after the override set it (it is kept). | | `tsconfig.restore-reapply` | `--restore` ran while the restored overrides are still configured. | Silence any of them with [`logger.suppress`](/guide/config/logging). When a sync finds **no** workspace tsconfig files, it warns (in the CLI logs and as a VS Code notification) if either: * `refs` or `composite` is enabled, or * a `tsconfig.workspaces.json` is present, because in those cases workspaces are clearly expected. Otherwise it logs a quiet "nothing to do" message. ``` no workspace tsconfig files found (looked for **/tsconfig.default.json), but project references + tsconfig.workspaces.json have nothing to wire. ``` ## The workspaces config `tsconfig.workspaces.json` holds the `paths` and `references` the generator expands. Scaffold it with `project:init:monorepo`, or build it programmatically from a workspace list: ```ts import { buildMonorepoTsconfig, renderMonorepoTsconfig } from '@deltabox/toolchain/tsconfig'; const config = buildMonorepoTsconfig({ scope: '@acme', workspaces: [ { alias: 'core', dir: 'packages/core' }, { alias: 'cli', dir: 'packages/cli' }, ], }); // -> { compilerOptions: { composite, paths: { '@acme/core': ['./packages/core/src/index.ts'], ... } }, // references: [{ path: './packages/core/tsconfig.json' }, ...] } renderMonorepoTsconfig({ scope: '@acme', workspaces: [{ alias: 'core', dir: 'packages/core' }] }); ``` ## CLI ```sh dbx tsconfig # regenerate every workspace tsconfig.json dbx tsconfig --no-aliases # skip the workspaces path aliases dbx tsconfig --refs # wire project references dbx tsconfig --composite # emit composite: true dbx tsconfig -i 2 # JSON indentation dbx tsconfig --glob "packages/*/tsconfig.default.json" "apps/*/tsconfig.default.json" dbx tsconfig --exclude "**/legacy/**" # exclude paths (on top of the defaults) dbx tsconfig --no-gitignore # do not respect .gitignore files dbx tsconfig --default-file tsconfig.src.json dbx tsconfig --build-file tsconfig.dist.json dbx tsconfig --root-file tsconfig.workspaces.json dbx tsconfig --restore [kind] # remove applied overrides (base|root|default|build) ``` Every flag overrides the matching `tsconfig` option; with no flags the command uses the config defaults (including in-memory `overrides`, so the output matches `dbx sync`). `dbx tsconfig --restore [kind]` removes applied overrides instead (see [Restoring](#restoring)). `dbx sync` runs the same generator through the [tsconfig sync task](#sync--ci). ### Resolving a tsconfig The same command can inspect a config instead of generating files. It follows `extends` per field, so a child can define `paths` while inheriting `references` (or the reverse): ```sh dbx tsconfig --json # { alias, paths, references } dbx tsconfig --alias # Vite/Vitest resolve.alias entries dbx tsconfig --paths # resolved compilerOptions.paths dbx tsconfig --references # resolved project references dbx tsconfig --tsconfig tsconfig.build.json dbx tsconfig --root packages/app # root used for detection + relative tsconfig dbx tsconfig --no-extends # only the selected file ``` The output is machine-clean JSON (absolute paths). Exact `alias` keys are strings; wildcards are anchored regular expressions (rendered as `/.../` strings in the CLI JSON), so `@acme/core` never captures `@acme/core-utils`. Exact keys sort before wildcards. ## Programmatic API ```ts import { generateTsconfigs, runTsconfigCli, resolveTsconfig, readTsconfig, renderTsconfigBase, renderTsconfigDefault, renderTsconfigBuild, renderTsconfigTemplates, buildMonorepoTsconfig, renderMonorepoTsconfig, tsconfigSyncTask, tsconfigFileNames, restoreTsconfigOverrides, } from '@deltabox/toolchain/tsconfig'; ``` | Export | Description | | ---------------------------------------------------------------------- | ------------------------------------------------------- | | `generateTsconfigs(options)` | Expand every default tsconfig (dry-run-able). | | `runTsconfigCli(argv?)` | The `dbx tsconfig` entry point. | | `resolveTsconfig(options?)` | Resolve `{ alias, paths, references }` (extends-aware). | | `readTsconfig(file)` | Read a JSONC tsconfig (comments + trailing commas). | | `renderTsconfigBase` / `renderTsconfigDefault` / `renderTsconfigBuild` | Render a template with replacements. | | `renderTsconfigTemplates(replacements?)` | Render every template keyed by file name. | | `buildMonorepoTsconfig(options)` / `renderMonorepoTsconfig(options)` | The workspaces config (object / JSON text). | | `tsconfigSyncTask()` | The sync task wired into `dbx sync`. | | `tsconfigFileNames(config)` | The configured base/root/default/build file names. | | `restoreTsconfigOverrides(options)` | Remove applied overrides (`{ cwd, kind?, dryRun? }`). | | `applyOverride` / `mergeOverride` / `revertOverride` | The deep-merge + snapshot-aware revert helpers. | | `getSnapshot` / `setSnapshot` / `deleteSnapshot` | Read/write the manifest snapshot tree by relative path. | `resolveTsconfig` accepts `{ tsconfig, cwd, root, extends, overrides }`: `tsconfig` is resolved from `root` (default `cwd`), and `extends` defaults to `true`. Paths are resolved against the defining config's `baseUrl` (or its directory), matching TypeScript. `generateTsconfigs` returns one result per workspace (with `newTsconfig`, `originalConfig`, ...); pass `write: false` for a dry run. The task-level options are documented in the [Programmatic API](/guide/api/package-reference#deltaboxtoolchaintsconfig) reference. `renderTsconfigBase` / `renderTsconfigDefault` / `renderTsconfigBuild` accept an optional second `extends` argument (`string | string[] | false`), and `renderTsconfigTemplates` accepts `{ base, default, build }` as its second argument. ## Sync & CI At a monorepo root with the section enabled, `dbx sync` runs the tsconfig task and reports the regenerated files: ``` tsconfig: regenerated 6 workspace tsconfig(s). ``` The task watches the workspaces tsconfig and every default tsconfig (custom `rootFile` / `defaultFile` names included), keeps the build tsconfigs, and only applies to `monorepo-root` projects. Run `dbx sync --check` in CI to fail when a generated `tsconfig.json` drifts from its inputs. ### Refresh aliases after builds Auto-wired aliases and references are derived from each workspace's `package.json` (`exports`, then `types`/`main`, then a source scan). If a build step can change a workspace manifest (a generated `package.json`, a rewritten export map, ...), run the tsconfig-only sync once **after all workspace builds**: ```sh dbx sync --only tsconfig ``` This regenerates the workspaces tsconfig and every managed `tsconfig.json` without running the other sync tasks, and it does not prune files owned by those tasks. Add it to the end of your root build script so the aliases always reflect the final `package.json` exports: ```json [package.json] { "scripts": { "build": "yarn workspaces foreach -A run build && dbx sync --only tsconfig" } } ``` The task is skipped when `toolchain.tsconfig` is omitted or `false` — including `dbx tsconfig` and `dbx sync --only tsconfig` — so a disabled section never generates files. A nested workspace that defines its own `tsconfig` section owns its generated file: the root still aliases/references it, but does not rewrite it. ## Per-project configs `toolchain.tsconfig` is repository-scoped: the root sync task expands every workspace `tsconfig.json`, so nested configs **do not inherit** it — inheriting it would make `dbx tsconfig` re-scan a workspace's own subtree. A nested config may still declare its own `tsconfig` section when it manages its own workspace tree (a nested monorepo); otherwise add the workspace's `tsconfig.default.json` and run `dbx tsconfig` / `dbx sync` from the root. Other sections still inherit as usual; see [Configuration → Nested configs](/guide/config/nested-configs) for the rules. ## Scaffolding `dbx project:init:monorepo` writes `tsconfig.base.json` + a `tsconfig.workspaces.json`; `dbx project:init:workspace` writes the workspace's `tsconfig.default.json` / `tsconfig.build.json` / `tsconfig.json` (and a `dbx.config.ts`). See the [Monorepo Guide](/guide/monorepo#tsconfig-generation) for the full flow. --- --- url: https://toolchain.deltabox.studio/guide/typescript.md --- # TypeScript `@deltabox/toolchain` is written in TypeScript and ships the config model, the generated `.d.ts` files and the ambient Zod metadata types. This page covers the type-safety story for consumers. ## `dbx.config.ts` `defineConfig` is an identity helper that gives the config file full autocomplete and type-checking, including the opt-in `toolchain.ci` workflows: ```ts [dbx.config.ts] import { defineConfig } from '@deltabox/toolchain'; export default defineConfig({ toolchain: { project: { name: 'acme', scope: '@acme' }, ci: { ci: true, changelog: { commitlint: { scopes: ['api', 'web'] }, versioning: { target: 'individual' }, }, }, }, }); ``` It accepts an object, a promise, or a sync/async callback — the loader resolves it (see [`defineConfig`](/guide/config/defineconfig)). ## Config types The `toolchain` shape is exported as `ToolchainConfig` (and the individual sections as `ToolchainCiConfig`, `ChangelogConfig`, ...): ```ts import type { ToolchainConfig, ToolchainCiConfig } from '@deltabox/toolchain'; const ci: ToolchainCiConfig = { ci: true, changelog: true }; const config: ToolchainConfig = { project: { name: 'acme' }, ci }; ``` The same types are available from `@deltabox/toolchain/shared`, and each subpath re-exports the types it owns. ## Ambient schema metadata Zod's `.meta()` is typed by default, but the toolchain adds its own fields (`env`, `path`, `required`, `deprecated`, `deprecationMessage`, `example`, `since`, `see`, `remarks`, `experimental`, `alpha`, `beta`, `secret`, `scope`, `public`, `defaultValue`). Add the ambient declaration once in a project-wide `.d.ts`: ```ts [env.d.ts] /// ``` Now a schema used with the [config generator](/guide/config-generator/) is fully typed: ```ts import { z } from 'zod'; export const serverConfig = z.object({ port: z.number().default(3000).meta({ env: 'MYAPP_PORT', description: 'HTTP port', example: '3000', }), password: z.string().meta({ env: 'MYAPP_AUTH_PASSWORD', required: true, secret: true, }), }); ``` `ConfigMeta` is `ConfigBaseMeta & ConfigEnvMeta`: * `ConfigBaseMeta`: `description`, `required`, `deprecated`, `deprecationMessage`, `example`, `since`, `see`, `remarks`, `experimental`, `alpha`, `beta`, `secret`, `scope`, `public`, `defaultValue`. * `ConfigEnvMeta`: `envName` and `envPath` (both optional, so non-env configs can use metadata too). `.describe()` is still the primary description and is rendered into the generated types, JSON Schema and markdown reference; `.meta({ description })` is used as a fallback and adds env/docs metadata. ## Programmatic API The loader and sync runner are typed end-to-end: ```ts import { defineConfig, defaultSyncTasks, loadToolchainConfig, runSync, type ToolchainConfig, } from '@deltabox/toolchain'; const loaded = await loadToolchainConfig(process.cwd()); const config: ToolchainConfig = loaded.config; await runSync({ tasks: defaultSyncTasks(), cwd: process.cwd(), config, tokens: loaded.tokens, projectType: loaded.projectType, }); // Run only a named task (eg. refresh the tsconfig aliases after a build). await runSync({ tasks: defaultSyncTasks(), only: ['tsconfig'], cwd: process.cwd(), config, tokens: loaded.tokens, projectType: loaded.projectType, }); ``` `only` implies `prune: false`, so a subset never deletes files owned by the tasks it skipped. See the [Programmatic API](/guide/api/) guide for the package-by-package reference. ## Vite+ The generated `vite.config.ts` shim is typed against the project's Vite package (`vite` or `vite-plus`) and `resolveVite` is generic, so `resolveVite({ cwd: import.meta.dirname })` keeps the full Vite+ surface (see [Vite+](/guide/vite)). ## JSON Schema / editor autocomplete [config.schema.json](/config.schema.json) is generated from the same Zod model and published at the site root, so a `dbx.config.json` gets editor completion. For a TypeScript config, `defineConfig` already provides the same information. --- --- url: https://toolchain.deltabox.studio/guide/vite.md --- # Vite+ The `vite` section holds the **entire Vite+ configuration**. Every key is passed through verbatim to Vite+, so `dbx.config.*` stays the only hand-authored root config; `dbx sync` writes a thin root `vite.config.ts` that resolves it at load time. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { vite: { fmt: { semi: true, singleQuote: true }, lint: { ignorePatterns: ['dist/**'] }, test: { include: ['src/**/*.test.ts'], environment: 'node' }, run: { tasks: { build: { command: 'tsdown --config-loader unrun' } } }, }, }, }); ``` The section is typed as Vite+'s own config (`ToolchainViteConfig` = Vite's `UserConfig` as augmented by Vite+), so the whole Vite+ surface autocompletes in `dbx.config.ts`. ## The generated shim `dbx sync` writes a fixed `vite.config.ts`: ```ts [vite.config.ts] import { defineConfig, type ViteUserConfig } from 'vite-plus'; // Generated by @deltabox/toolchain — edit the vite section in dbx.config and run `dbx sync`. import { resolveVite } from '@deltabox/toolchain/vite'; export default defineConfig(resolveVite({ cwd: import.meta.dirname })); ``` The shim imports `defineConfig` from whichever Vite package the project installs — `vite` or `vite-plus` (detected from `package.json`, walking up so a hoisted workspace dependency is found). For a plain `vite` project it imports `defineConfig, type UserConfig` from `vite` and calls `resolveVite`, so the same shim type-checks against both. `resolveVite(options?)` resolves the **same-directory** `dbx.config.*` (so a nested workspace `vite.config.ts` uses its own config), or the `toolchain` you pass, then returns `toolchain.vite`. You can also pass inline overrides with `resolveVite({ vite: { ... } })`, which are deep-merged over the loaded section. The file never drifts, because the real config stays in `dbx.config.*`. The root shim must live at the **root**: `vp run`, `vp check` and `vp staged` do not accept `--config`, so the shim has to be the file Vite+ loads. ## Options | Option | Type | Default | Description | | -------- | ------------------- | ------- | ----------------------------------------------- | | `config` | `boolean \| string` | `true` | Write the managed shim; a string sets its path. | Everything else under `vite` is Vite+ config (plugins, `test`, `lint`, `fmt`, `run.tasks`, ...). `vite.fmt` / `vite.lint` override `toolchain.fmt.oxc` / `toolchain.lint.oxc` (deep merge). `fmt: true` / `lint: true` use the shared `DEFAULT_FMT_RULES` / `DEFAULT_LINT_RULES`; an options object is merged over them. Their ignore lists are exported as `DEFAULT_FMT_IGNORE_PATTERNS` / `DEFAULT_LINT_IGNORE_PATTERNS` (both layered over `DEFAULT_IGNORE_PATTERNS`) from `@deltabox/toolchain` and `@deltabox/toolchain/shared`, so you can extend them instead of repeating the defaults. Import sorting is part of those defaults: `sortImports` gets a `workspace` custom group resolved from every dbx project (see `resolveFmtCustomGroups`), so workspace packages do not need manual groups. Set `vite: { config: false }` to leave a hand-written `vite.config.ts` alone (the section is still read for provider colouring and the `dbx` provider passthrough). `config: 'config/vite.custom.ts'` writes the shim there instead (relative to the project, or an absolute path inside it). ## Keep the shim out of Oxfmt Add the shim to `vite.fmt.ignorePatterns`, otherwise `vp fmt` rewrites the generated file and `dbx sync --check` reports drift: ```ts vite: { fmt: { ignorePatterns: ['/vite.config.ts']; } } ``` In a monorepo, put `**/vite.config.ts` on the **root** config so every workspace shim is covered (workspace `fmt` settings are inherited, but an `ignorePatterns` array in a workspace replaces the root's list). `dbx migrate --from vite` adds the ignore for you (see [Migrations](/guide/migrations/#vite-config)). When `fmt` / `lint` are defined, `dbx sync` also writes `.oxfmtrc.json` / `.oxlintrc.json` for the Oxc VS Code extension. They are root-only unless a workspace config defines its own `fmt` / `lint`. ## Commands Vite+ commands are reachable directly or through the `vite` namespace, and are registered as a provider (see [Providers](/guide/providers)): ```sh dbx fmt # the active formatter (vp fmt when Oxc) dbx run -r build # forwards to `vp run -r build` dbx vite dev # explicit namespace (always works) dbx vite:build # named alias for `vp build` ``` A bare `dbx ` is forwarded to Vite+ when the name (or a Vite+ alias) matches — built-in commands win, so `dbx migrate` stays the toolchain's migration. `dbx fmt` / `dbx lint` route to the configured/discovered Biome/Prettier/Dprint/ESLint tool, and keep the `vp` passthrough when the active tool is Oxc (see [Formatting & linting](/guide/formatting#running-the-active-tool)). ## Programmatic API ```ts import { resolveVite, renderViteConfig, viteConfigObject, viteProvider, viteSyncTask, } from '@deltabox/toolchain/vite'; ``` | Export | Description | | ------------------------------ | --------------------------------------------------------------------------------- | | `resolveVite(options?)` | Resolve `toolchain.vite` (or a passed `toolchain`), with inline `vite` overrides. | | `detectViteSpecifier(cwd)` | Which package (`vite` / `vite-plus`) the project has, for the shim import. | | `renderViteConfig(specifier?)` | The managed `vite.config.ts` shim source (defaults to `vite-plus`). | | `viteConfigObject(config?)` | The `vite` section without the toolchain opt-out `config`. | | `viteSyncTask()` | The sync task (writes the shim). | | `viteProvider()` | The Vite+ provider descriptor. | See [Configuration](/guide/config/vite) for the `dbx.config` integration and the [Providers](/guide/providers) guide for the passthrough commands. --- --- url: https://toolchain.deltabox.studio/guide/watch.md --- # Watch The `watch` section configures `dbx watch`, which syncs once and then re-runs on input changes. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { watch: { enabled: true, debounce: 150 }, }, }); ``` ## Options | Option | Type | Default | Description | | ----------- | -------------------- | -------------------- | ------------------------------------------- | | `enabled` | `boolean` | enabled when defined | Watch by default. | | `debounce` | `number` | `150` | Debounce window in ms before a re-sync. | | `exclude` | `string \| string[]` | common ignored paths | Glob(s) excluded from watching. | | `gitignore` | `boolean` | `true` | Respect `.gitignore` files (root + nested). | `exclude` adds to the common ignored paths (`node_modules`, `.dbx`, `dist`, `build`, ...), and `gitignore` also honours the repo's `.gitignore` files (root and nested, each scoped to its own directory) so ignored directories are never watched. Set `gitignore: false` to watch them anyway. The common list is exported as `DEFAULT_IGNORE_PATTERNS` from `@deltabox/toolchain` and `@deltabox/toolchain/shared` if you need to build on it. ```ts watch: { exclude: ['**/fixtures/**'], gitignore: true } ``` ## How it works ```sh dbx watch ``` `dbx watch` runs one sync, then watches the config file plus every task's `watch` globs and re-runs on change. A config change reloads the config (and rebuilds the watched inputs) before re-syncing. Writes made by the sync itself must not re-trigger a run, so a short quiet window follows each sync. Only **input** files are watched (eg. `dbx.config.*`, `tsconfig.default.json`, `tsconfig.workspaces.json`), never the generated outputs — so a sync can never loop on its own writes. Custom tsconfig names (`tsconfig.rootFile`, `tsconfig.defaultFile`) are watched instead of the defaults. `dbx watch --list` prints the resolved inputs without watching (`--json` adds the tsconfig file names; the VS Code extension reads it): ```sh dbx watch --list --json # { "globs": ["**/dbx.config.*", "tsconfig.workspaces.json", "**/tsconfig.default.json"], # "tsconfig": { "baseFile": "tsconfig.base.json", ... } } ``` `dbx watch --force` runs even when there is no `dbx.config.*`, and `watch: false` / `watch: { enabled: false }` disables it (so a plain `dbx watch` warns and exits). ## VS Code The VS Code extension has its own debounced scheduler that mirrors this behaviour (see [VS Code Extension](/guide/vscode#auto-sync)); the `watch` section configures the **CLI** `dbx watch` command. --- --- url: https://toolchain.deltabox.studio/CHANGELOG.md --- # Changelog ## 2.6.1 (November 2026) ### Bug fixes #### Changelog * Scope package changes by package name ([#0ee9b9e](https://github.com/deltabox-studio/toolchain/commit/0ee9b9e) by [T0chi](https://github.com/T0chi)) * key dependency changes by the declaring package.json name so workspace-specific pins (eg. the docs site's Vite 5) are reported under that workspace * scope the catalog to the root package name; keep workspace catalog: refs unresolved so a catalog bump is reported once * add regression tests and regenerate every CHANGELOG.md with the scoped footer ## 2.6.0 (November 2026) ### New features * Route fmt/lint to the active tool and opt scaffolds into catalogs ([#76df575](https://github.com/deltabox-studio/toolchain/commit/76df575) by [T0chi](https://github.com/T0chi)) * dbx fmt/lint resolve the configured/discovered formatter or linter (oxc > biome > prettier > dprint; oxc > biome > eslint) and run it, falling back to the vp/Oxc passthrough * project:init/create scaffolds add the catalogs section for the catalog file they write, so dbx sync owns it instead of leaving the managed file orphaned * document the active-tool routing and add the dedicated package catalogs guide ## 2.5.0 (November 2026) ### New features #### Config * Add a reusable CLI docs generator ([#900566a](https://github.com/deltabox-studio/toolchain/commit/900566a) by [T0chi](https://github.com/T0chi)) * add generateCliDocs(commands, options?) for custom command descriptors * use it to render the dbx CLI reference * document generating docs for a consumer's own command list * cover hidden/duplicate commands, args/options and custom prefix/title * Add self-contained vendor types and `tsconfig` auto/`rewriteAlias` schemas ([#31c47a9](https://github.com/deltabox-studio/toolchain/commit/31c47a9) by [T0chi](https://github.com/T0chi)) #### Changelog * Inject provider setup into custom and vendor workflows ([#b6b240f](https://github.com/deltabox-studio/toolchain/commit/b6b240f) by [T0chi](https://github.com/T0chi)) * add an opt-in setup flag to custom workflow options and vendor settings * GitHub injects the missing checkout/Node/Corepack/install steps * GitLab adds a Node image + before_script; Azure adds UseNode@1 + install * avoid duplicating setup steps the workflow already defines * resolve vendor workflow tokens from the default token set #### Biome * Add array removal directives and resolve toolchain debt ([#da100c8](https://github.com/deltabox-studio/toolchain/commit/da100c8) by [T0chi](https://github.com/T0chi)) * add $remove: / $reset array directives with a resolver step for biome.json * fix peer warnings (declare vite, filter the stale VitePress plugin peer) * replace the RULE_MAP cast with an Object.hasOwn guard * add a direct extension env-propagation test * drop the 30s real-git changelog timeout #### Cli * Add `sync --only` task selection and `selectSyncTasks` ([#49d73dd](https://github.com/deltabox-studio/toolchain/commit/49d73dd) by [T0chi](https://github.com/T0chi)) #### Vscode * Split extension modules and add `syncTsconfig` action ([#f121c47](https://github.com/deltabox-studio/toolchain/commit/f121c47) by [T0chi](https://github.com/T0chi)) #### Tsconfig * Auto-wire aliases/references with `rewriteAlias` and gating ([#124f4b5](https://github.com/deltabox-studio/toolchain/commit/124f4b5) by [T0chi](https://github.com/T0chi)) ### Bug fixes #### Cli * Sync and watch configless projects with the inherited config ([#346060d](https://github.com/deltabox-studio/toolchain/commit/346060d) by [T0chi](https://github.com/T0chi)) * The command context did not expose the ancestor config path, so dbx sync/watch in a nested project without its own dbx.config.\* warned and did nothing. Pass parentPath through the context and allow both commands when it is set. The VS Code Sync all compensation now works for configless projects, while sync --recursive stays config-bearing-only and is documented as such. ### Documentation * Document watcher scoping and project identity inheritance ([#074bfcd](https://github.com/deltabox-studio/toolchain/commit/074bfcd) by [T0chi](https://github.com/T0chi)) * Update the VS Code auto-sync section and the project guide to match the implementation: watchers are root-scoped and sync only the changed root; project name/scope/description/type are never inherited. Regenerate llms-full.txt. #### Tsconfig * Note that refsAuto requires tsc --build ([#74a25b2](https://github.com/deltabox-studio/toolchain/commit/74a25b2) by [T0chi](https://github.com/T0chi)) * Project references are only followed by tsc --build; tsc -p --noEmit ignores them. Document the refsAuto requirement and why this repo keeps the references hand-written (refsAuto: false). #### Config * Document every generator and generateCliDocs ([#28f812e](https://github.com/deltabox-studio/toolchain/commit/28f812e) by [T0chi](https://github.com/T0chi)) * add engine + standalone examples for each generator * document CLI docs, post-processing and advanced options * cross-link generateCliDocs from the config-generator index and CLI commands #### Vscode * Expand the extension guide and README ([#942aa55](https://github.com/deltabox-studio/toolchain/commit/942aa55) by [T0chi](https://github.com/T0chi)) * Restore the updated provider/template sections, settings and auto-sync documentation in both the guide and the extension README. ## 2.4.0 (November 2026) ### New features #### Shared * Improve workflow config autocomplete ([#5babd18](https://github.com/deltabox-studio/toolchain/commit/5babd18) by [T0chi](https://github.com/T0chi)) * Add typed WorkflowTriggers, WorkflowExtra and WorkflowTokens shapes so known workflow keys are suggested, and regenerate the config types, schema and reference. * Expose formatter and linter ignore defaults ([#5f2c8b1](https://github.com/deltabox-studio/toolchain/commit/5f2c8b1) by [T0chi](https://github.com/T0chi)) * Export DEFAULT_FMT_IGNORE_PATTERNS and DEFAULT_LINT_IGNORE_PATTERNS alongside DEFAULT_IGNORE_PATTERNS, and document how to extend them. #### Project * Add config generation options and migration routing ([#c658b0d](https://github.com/deltabox-studio/toolchain/commit/c658b0d) by [T0chi](https://github.com/T0chi)) * Support --skip-config, --language and --config-extension, write the project graph before configs, generate TS/JS configs, and route migrated fmt/lint into toolchain.vite only when the existing Vite config owns them. #### Vscode * Add tooling actions and config generation ([#31da418](https://github.com/deltabox-studio/toolchain/commit/31da418) by [T0chi](https://github.com/T0chi)) * Add the format import-group setting, migrate/global Home actions, and config generation prompts (TS/JS, graph-only) that also work from the Command Palette. #### Core * Add import sorting defaults and workspace groups ([#1c5f5a3](https://github.com/deltabox-studio/toolchain/commit/1c5f5a3) by [T0chi](https://github.com/T0chi)) * Enable sortImports by default, resolve a workspace custom group from the project graph, and inject it into resolveVite, vite sync and tooling normalization. ### Bug fixes #### Shared * Repair PowerShell backtick artifacts in changelogs ([#61ced92](https://github.com/deltabox-studio/toolchain/commit/61ced92) by [T0chi](https://github.com/T0chi)) * Repair paired \code\ and lone trailing identifier backslashes back to inline code while leaving paths and regexes alone, and correct the affected 2.3.1-2.3.3 entries. ## 2.3.0 (November 2026) ### New features #### Changelog * Dispatch workflows with their vendor CLI ([#eea38ae](https://github.com/deltabox-studio/toolchain/commit/eea38ae) by [T0chi](https://github.com/T0chi)) #### Config * Type tool options and changelog callbacks ([#10bd933](https://github.com/deltabox-studio/toolchain/commit/10bd933) by [T0chi](https://github.com/T0chi)) ### Bug fixes #### Config * Escape CLI reference tables and fail on missing output ([#15978be](https://github.com/deltabox-studio/toolchain/commit/15978be) by [T0chi](https://github.com/T0chi)) #### Core * Keep managed paths inside the project root ([#2496bee](https://github.com/deltabox-studio/toolchain/commit/2496bee) by [T0chi](https://github.com/T0chi)) #### Changelog * Preserve hand-corrected text in `--format-only` ([#2041e5f](https://github.com/deltabox-studio/toolchain/commit/2041e5f) by [T0chi](https://github.com/T0chi)) #### Shared * Restore `PowerShell`-escaped letters ([#4d2be6a](https://github.com/deltabox-studio/toolchain/commit/4d2be6a) by [T0chi](https://github.com/T0chi)) ### Documentation * Document vendor workflows, typed builders and `--format-only` ([#eb66982](https://github.com/deltabox-studio/toolchain/commit/eb66982) by [T0chi](https://github.com/T0chi)) #### Changelog * Document `formatLine` ([#61e28ec](https://github.com/deltabox-studio/toolchain/commit/61e28ec) by [T0chi](https://github.com/T0chi)) ### Chores #### Config * Regenerate config-types, schema and docs ([#0b0ca20](https://github.com/deltabox-studio/toolchain/commit/0b0ca20) by [T0chi](https://github.com/T0chi))