Owns the Node 25 install used by tools that don't run on Bun. The dean-stack app itself never executes on Node — it ships as static files to GitHub Pages. Node exists here strictly to host build/CI tools that haven't been ported to Bun.
When to invoke
- Asked to set Node version for the repo (
.nvmrc, engines.node).
- A tool refuses to run on Bun and you need to confirm it can run on Node 25.
- CI workflow needs
actions/setup-node.
- Diagnosing a "tool requires Node X" error during install or build.
- Someone asks whether to add
.tool-versions, a volta block, or otherwise "enforce" a version manager — see below.
Owns
Node 25 install, version pinning via .nvmrc, the actions/setup-node invocation in CI, and the rule that Node is only for tooling — never a runtime target.
Defers to
bun — for everything that can run on Bun (almost everything in this stack).
bun-package-manager — for installing deps (bun install, not npm install).
nitro — for build-time tooling that may bridge through Node (CI uses Node 20+ per the upstream Nitro doc).
turborepo — for orchestrating Node-hosted tools alongside Bun-hosted ones.
Dean-stack rules
- Pillar 4 (CLI-gate-first) means: if a Node-hosted tool is in the gate, its CLI must obey the same zero-warning rule (no warnings, non-zero on any finding).
- Node is tooling-only. The app runtime is the browser; the script runtime is Bun. Do not introduce a
node server, a node:test suite, or a Node-hosted dev server.
.nvmrc is the single source of truth for the Node version. CI reads it (actions/setup-node@v4 with node-version-file: ".nvmrc"); locally, contributors use nvm. The repo does NOT ship a parallel .tool-versions file or a volta block in package.json — adding either would create a second pin to keep in sync.
- The version manager itself is a per-developer concern, not a project dep — it's a shell tool installed globally, never an npm dependency.
Patterns
.nvmrc for the repo (the only Node pin)
25
A single major number. CI's actions/setup-node reads this; local nvm reads it via nvm use. Don't add a second pin elsewhere.
CI: install Node alongside Bun
# .github/workflows/check.yml (excerpt)
- uses: actions/setup-node@v4
with: { node-version-file: ".nvmrc" }
- uses: oven-sh/setup-bun@v2
with: { bun-version-file: "package.json" }
- run: bun install --frozen-lockfile
- run: bun run check
Both runtimes coexist. Bun is the executor for bun run check; Node is available for any tool the gate invokes that demands it. CI doesn't use any version-manager wrapper — actions/setup-node reads .nvmrc directly.
When a tool demands Node
- Verify it actually fails on Bun (try
bunx <tool> first).
- If it does, invoke it via a Node binary explicitly:
node ./node_modules/<tool>/bin/<tool>.
- Wrap that in a
bun run-able npm script so Turbo still orchestrates it.
- Document the Node-dependency in a script comment.
Anti-patterns
- Don't add a
.tool-versions file — .nvmrc already pins Node, and asdf-nodejs supports .nvmrc natively via its legacy_version_file setting. A second pin file is a maintenance hazard, not a feature.
- Don't add a
"volta": {...} block to package.json — Volta reads .nvmrc for the Node version on its own, and Bun is already pinned via packageManager. The block would duplicate both pins.
- Don't add asdf, Volta, nvm, fnm, or any version manager to
package.json deps — they're shell tools installed globally per developer, not npm packages. They cannot be peerDependencies and should not be devDependencies.
- Don't write a
node server — the app is static (see nitro for the GH Pages preset).
- Don't use
node:test — bun test is the unit-test runner (see bun-test).
- Don't use
npm/pnpm/yarn to install — Bun owns the package manager (see bun-package-manager).
- Don't pin Node below 22 — Node 22 LTS is the floor; 25 Current is the target.
- Don't recommend
nodemon/tsx/dotenv — Bun covers these. If the user has Node-only tooling, use Node directly, but don't reach for the Node convenience wrappers.
Triggers on
node 25, node version, .nvmrc, .tool-versions, volta, asdf, node tooling, node toolchain
1---2name: node3description: Node 25 in dean-stack — installed only for tools that refuse to run on Bun, pinned via `.nvmrc` (the single source of truth). The repo does NOT ship `.tool-versions` or a `volta` block; the per-developer version manager is each contributor's choice. Node is a tooling runtime, never an application target. Triggers on: node 25, node version, .nvmrc, .tool-versions, volta, asdf, node tooling, node toolchain.4license: MIT5---67Owns the Node 25 install used by tools that don't run on Bun. The dean-stack app itself never executes on Node — it ships as static files to GitHub Pages. Node exists here strictly to host build/CI tools that haven't been ported to Bun.89## When to invoke10- Asked to set Node version for the repo (`.nvmrc`, `engines.node`).11- A tool refuses to run on Bun and you need to confirm it can run on Node 25.12- CI workflow needs `actions/setup-node`.13- Diagnosing a "tool requires Node X" error during install or build.14- Someone asks whether to add `.tool-versions`, a `volta` block, or otherwise "enforce" a version manager — see below.1516## Owns17Node 25 install, version pinning via `.nvmrc`, the `actions/setup-node` invocation in CI, and the rule that Node is **only** for tooling — never a runtime target.1819## Defers to20- `bun` — for everything that *can* run on Bun (almost everything in this stack).21- `bun-package-manager` — for installing deps (`bun install`, not `npm install`).22- `nitro` — for build-time tooling that may bridge through Node (CI uses Node 20+ per the upstream Nitro doc).23- `turborepo` — for orchestrating Node-hosted tools alongside Bun-hosted ones.2425## Dean-stack rules26- Pillar 4 (CLI-gate-first) means: if a Node-hosted tool is in the gate, its CLI must obey the same zero-warning rule (no warnings, non-zero on any finding).27- Node is **tooling-only**. The app runtime is the browser; the script runtime is Bun. Do not introduce a `node` server, a `node:test` suite, or a Node-hosted dev server.28- **`.nvmrc` is the single source of truth for the Node version.** CI reads it (`actions/setup-node@v4` with `node-version-file: ".nvmrc"`); locally, contributors use `nvm`. The repo does NOT ship a parallel `.tool-versions` file or a `volta` block in `package.json` — adding either would create a second pin to keep in sync.29- The version manager itself is a **per-developer concern**, not a project dep — it's a shell tool installed globally, never an npm dependency.3031## Patterns3233### `.nvmrc` for the repo (the only Node pin)34```352536```37A single major number. CI's `actions/setup-node` reads this; local `nvm` reads it via `nvm use`. Don't add a second pin elsewhere.3839### CI: install Node alongside Bun40```yaml41# .github/workflows/check.yml (excerpt)42- uses: actions/setup-node@v443 with: { node-version-file: ".nvmrc" }44- uses: oven-sh/setup-bun@v245 with: { bun-version-file: "package.json" }46- run: bun install --frozen-lockfile47- run: bun run check48```49Both runtimes coexist. Bun is the executor for `bun run check`; Node is available for any tool the gate invokes that demands it. CI doesn't use any version-manager wrapper — `actions/setup-node` reads `.nvmrc` directly.5051### When a tool demands Node521. Verify it actually fails on Bun (try `bunx <tool>` first).532. If it does, invoke it via a Node binary explicitly: `node ./node_modules/<tool>/bin/<tool>`.543. Wrap that in a `bun run`-able npm script so Turbo still orchestrates it.554. Document the Node-dependency in a script comment.5657## Anti-patterns58- **Don't add a `.tool-versions` file** — `.nvmrc` already pins Node, and asdf-nodejs supports `.nvmrc` natively via its `legacy_version_file` setting. A second pin file is a maintenance hazard, not a feature.59- **Don't add a `"volta": {...}` block to `package.json`** — Volta reads `.nvmrc` for the Node version on its own, and Bun is already pinned via `packageManager`. The block would duplicate both pins.60- **Don't add asdf, Volta, nvm, fnm, or any version manager to `package.json` deps** — they're shell tools installed globally per developer, not npm packages. They cannot be `peerDependencies` and should not be `devDependencies`.61- **Don't write a `node` server** — the app is static (see `nitro` for the GH Pages preset).62- **Don't use `node:test`** — `bun test` is the unit-test runner (see `bun-test`).63- **Don't use `npm`/`pnpm`/`yarn`** to install — Bun owns the package manager (see `bun-package-manager`).64- **Don't pin Node below 22** — Node 22 LTS is the floor; 25 Current is the target.65- **Don't recommend `nodemon`/`tsx`/`dotenv`** — Bun covers these. If the user has Node-only tooling, use Node directly, but don't reach for the Node convenience wrappers.6667## Triggers on68node 25, node version, .nvmrc, .tool-versions, volta, asdf, node tooling, node toolchain