This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
@uscreen.de/create-fastify-app is a pnpm create CLI that scaffolds a new @uscreen.de/fastify-app (run as pnpm create @uscreen.de/fastify-app <name>). It has two distinct halves that are easy to conflate:
bin/cli.js— the entire CLI logic, a single file. This is the tool itself.skeleton/— template files copied verbatim into every generated app. This is not part of the tool's runtime; it is product output. Itspackage.jsondeclares the generated app's deps/scripts (fastify 5,@uscreen.de/fastify-app, etc.), not the CLI's.
When editing, always be clear which half you're in. Changes under skeleton/ ship to every newly created app.
pnpm test # all tests, spec reporter (node:test)
pnpm test:cov # + c8 HTML/text coverage
node --test --test-reporter spec test/cli.test.js # a single test file
pnpm lint # eslint check
pnpm lint:fix # eslint autofix- Tests use the Node.js built-in runner (
node:test) withnode:assert/strict— no Jest/Mocha. Coverage isc8. pnpm install/pnpm devinstructions in the README refer to the generated app, not this repo.
bin/cli.js defines one commander command taking <name>, then runs five steps in order against path.resolve(cwd, name):
ensurePath—fs.ensureDirthe target dirinitializePnpm—spawn('pnpm', ['init'])in it (inherits stdio, so its output is what tests assert against)addPackageConfig— read both package.jsons viaread-package-up, overlay the skeleton'smain/type/scripts/dependencies/devDependenciesonto the freshly-init'd one, thenwritePackage. Also strips_id,readme, anddevEngines(the last is injected bypnpm initand its caret range breakspnpm install— there is a regression test for this).copySkeleton—fs.copy(skeleton, target, { overwrite: false })copyEnv— copyskeleton/.env.exampleto the app's.env
There is no flag/option surface beyond <name> and --version; the old -y flag is gone (it's the default).
test/setup.js provides cli(args), which execs bin/cli.js in a temp test/_arena/ dir and resolves { code, error, stdout, stderr }. before/after create and remove _arena/. Tests assert on real CLI output and on files written to disk, so they actually invoke pnpm init — pnpm must be on PATH.
- Skeleton tests need
--ignore-workspace. The repo root has apnpm-workspace.yaml, so a plainpnpm installinsideskeleton/(or a generated app under this tree) silently no-ops. Usepnpm install --ignore-workspacewhen working inside the skeleton. skeleton/is excluded from linting (seeeslint.config.jsignores). Don't expect lint to catch issues there.- pnpm only (no npm/yarn). Node 20+; CI matrix is 20/22/24,
.nvmrcpins 24.
ESLint flat config via @antfu/eslint-config with formatters: true: no semicolons, no trailing commas, single quotes, 2-space indent. ESM only ("type": "module") — local imports keep the .js extension, core modules use the node: prefix. no-console is off (it's a CLI). Prefer fs-extra over native fs.