Running nub init scaffolds a minimal TypeScript project that runs and typechecks immediately. It writes five files, initializes git, and installs TypeScript plus the type packages the editor needs:

$ nub init -y
created my-app
├── package.json
├── tsconfig.json
├── index.ts
├── .gitignore
├── README.md
└── .git/

devDependencies:
+ @nubjs/types@0.4.13
+ @types/node@26.1.1
+ typescript@7.0.2

Your project is ready!
Try running `nub index.ts`

The scaffold has zero runtime dependencies — the entry file runs right away:

$ nub index.ts
Hello from Nub

What it writes

The manifest carries the same packageManager pin and devEngines range that nub pm pin writes, plus the dev dependencies:

package.json
{
  "name": "my-app",
  "version": "0.0.1",
  "type": "module",
  "packageManager": "nub@0.5.0",
  "devEngines": {
    "packageManager": { "name": "nub", "version": "^0.5.0", "onFail": "ignore" }
  },
  "scripts": {
    "start": "nub index.ts"
  },
  "devDependencies": {
    "@nubjs/types": ">=0.4.13 <=0.5.0",
    "@types/node": "^26",
    "typescript": "^7"
  }
}

The @nubjs/types range is capped at the running Nub version, but allows an older release so the default 24-hour cooling window can select the newest mature declarations. The install step resolves the dependencies and writes Nub's neutral lockfile, nub.lock.

The tsconfig targets Node's own TypeScript semantics — nodenext resolution, verbatimModuleSyntax, strict mode, noEmit (Nub transpiles; the installed typescript is for the editor and typechecking) — and wires the installed type packages:

tsconfig.json
{
  "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "target": "es2024",
    "lib": ["es2024"],
    "types": ["node", "@nubjs/types"],
    "strict": true,
    "noEmit": true
    // ...
  }
}

The generated project is not locked to Nub: everything runs on plain Node too (the devEngines policy is ignore — a signal to read, not a rule to enforce), and swapping the start script to a plain node invocation is the only change a non-Nub user would make. Plain node runs the TypeScript entry on Node ≥22.18 (type stripping, unflagged in 23.6 and backported to 22.18); on older Node, scaffold with --js instead.

Prompts

In a terminal, nub init asks three questions — project name, TypeScript or JavaScript, and whether to initialize git. Every prompt has a sensible default; -y skips them all. Outside a terminal (CI, piped stdin) the defaults apply automatically, so the command never hangs.

Flags

FlagEffect
-y, --yesSkip all prompts, take the defaults.
--jsJavaScript variant: writes index.js, no tsconfig, no devDependencies.
--name <name>Project name (default: the directory name, sanitized). Scoped names (@scope/pkg) are preserved.
--no-gitSkip git init.
--no-installSkip the install step.
--forceOverwrite existing files.

Existing files

When any target file already exists, init refuses and names the conflicts — nothing is partially written:

$ nub init -y
Error: nub: refusing to overwrite existing files: package.json, tsconfig.json, index.ts, .gitignore, README.md
  (pass --force to overwrite)

Passing --force overwrites the listed files. An existing .git/ is never touched — init skips git init inside an existing repository.

Templates

Framework templates are not init's job — they belong to nub create. It resolves the ecosystem's create-* convention (vuecreate-vue, @scope/foo@scope/create-foo), fetches the scaffolder, and runs it. Scaffolders that detect the invoking package manager answer with Nub commands:

$ nub create vue my-app --default

└  Done. Now run:

   cd my-app
   nub install
   nub run dev

Everything after the template name is passed through to the scaffolder, so each create-* tool's own flags work unchanged.

Read the full docs for pnpm create on pnpm.io.

Passing a template to init points you the same way:

$ nub init vite
Error: nub: `init` does not accept arguments (got "vite")
  (to scaffold from a template: nubx create-vite)