The atoll CLI

@atolljs/cli scaffolds pools, shared-memory contracts, and worker-rendered islands — and audits the setup. Source-level generator: it writes readable files into your project, nothing it emits is a runtime dependency.

Experimental. The command grammar and generated code are still evolving — expect breaking changes between minor releases. Bugs and feedback: github.com/jwhenry3/atolljs/issues.

Run it

npx @atolljs/cli <command>
# or, once installed: atoll <command>

Requires Node ≥ 22.18 — the bin ships as TypeScript and runs under Node's native type stripping, so there's no build step. The generated app code runs on Node ≥ 20 and in browsers.

atoll new <dir>

Scaffold a fresh app — a Vite shell with COOP/COEP headers already set, a worker-rendered counter island, and a hardened .npmrc:

atoll new my-app --framework react   # react | vue | solid | svelte | node
atoll new my-api --framework node    # tsx Node service with a pooled worker

--pm npm picks the install command. Angular apps scaffold through the Angular CLI instead — ng new, then atoll init.

atoll init

Wire Atoll into an existing project: writes the supply-chain .npmrc (min-release-age=7, ignore-scripts), installs the @atolljs/* packages your detected framework needs, and generates a working spine — src/atoll/app.memory.ts, app.worker.ts, and a typed app.ts client.

atoll add [framework] <kind> [variant] [name]

Generate a piece into the current project. The leading framework word overrides project detection — atoll add react island facade counter. Kinds without a framework scope run anywhere:

kindgenerates
workerpooled worker + typed client (any host) — + framework bindings on UI projects
memorya *.memory.ts shared-memory contract module
islandworker-rendered UI — facade (contract + lazy import) or mono variant
servicenestjs — @AtollService class facade + registerPool module + worker entry
methodnestjs — @AtollTask method-level offload on an existing service
modulenestjs — registerPool boundary module + worker entry
housednestjs — route subtree served entirely inside workers (+ gateway wiring)
routenextjs — app/api/<name>/ route pool — task or client variant
componentnextjs — 'use client' component bound to a pool via hooks
instrumentationnextjs — server bootstrap that warms route pools at boot

Variants resolve as a positional (add island facade counter), --variant, an interactive pick, or the first listed non-interactively. A new worker wires the lone *.memory.ts in its directory automatically — --memory <name> pins one, --no-memory opts out.

atoll doctor [--fix]

Audits the setup — each check maps to an invariant in these docs:

  • Node ≥ 22.18 for the CLI bin, ≥ 20 for the runtime it generates
  • @atolljs/* dependencies present and a lockfile committed
  • .npmrc supply-chain policy — --fix writes it
  • worker entries bundler-detectable — every new Worker( must take new URL('./x.worker.ts', import.meta.url) inline so the bundler can see it
  • COOP/COEP headers in the Vite config — the SharedArrayBuffer gate (skipped for Node projects)

Flags & behavior

--yes / -yaccept every default — implied when there's no TTY
--forceoverwrite existing files (they're never touched otherwise)
--no-installwrite files but skip the dependency install
--dir <path>output directory for add (default src/atoll/)

Missing answers prompt interactively. The bootstrap install runs once with --min-release-age=0 — the just-written .npmrc would otherwise block the brand-new packages it installs; the policy governs every install after that.