Start a new project
firenook init sets up a new project that runs every emulator: a
firebase.json, a project id, rules, indexes and a Functions codebase in
TypeScript. In a terminal it asks on one screen, then installs the packages,
pins Firenook, builds your code once and offers to start the emulators. It
needs no Google account. If your project already has a firebase.json,
switch it over instead.
Create the project
Section titled “Create the project”In an empty folder, run init through your package manager:
npx firenook initpnpm dlx firenook inityarn dlx firenook initbunx firenook initThis runs the latest release, on any Node from 20 up, and init pins that
release in the folder’s package.json, so every later command runs the same
one. A folder that already has a package.json keeps it: init adds
Firenook and an emulators script, and keeps a pin or a script that is
already there.
One screen of choices
Section titled “One screen of choices”In a terminal, init opens on one screen with every choice already made, so
Enter takes them all:
╭──────────────────────────────────────────────────────────────────────────╮ │ │ │ ▄█ │ │ ▄███▀ ▄ firenook 0.2.0 │ │ ▄███▀ ███▄ a new project in ~/my-app │ │ ▀██▀ ███▀ Node 24.20.0 · Claude Code found │ │ █▀▀ │ │ │ ├─ New project ────────────────────────────────────────────────────────────┤ │ │ │ Project ID › demo-my-app │ │ demo- keeps it on this machine: no request can reach a │ │ real Firebase project │ │ Emulators ◉ Firestore 8080 ◉ Auth 9099 ◉ Storage 9199 │ │ ◉ Pub/Sub 8085 ◉ Console 4000 │ │ Functions ● TypeScript ○ JavaScript ○ None │ │ Agents ◉ Claude Code ○ Cursor ○ VS Code │ │ Set up ◉ Install packages ◉ Pin Firenook │ │ │ ╰──────────────────────────────────────────────────────────────────────────╯
↑↓ field ←→ choice space toggle enter create esc cancel| Row | What it chooses |
|---|---|
| Project ID | demo- plus the folder’s name. Type another id to use your real project’s |
| Emulators | Firestore, Auth, Storage, Pub/Sub and the console, each on its official port. A port something already listens on is named, with the program that holds it |
| Functions | A TypeScript codebase, a JavaScript one, or none. Cloud Tasks and Eventarc come with Functions |
| Agents | The coding agents the folder shows (.claude/, .cursor/, .vscode/, or claude on the PATH), which get Firenook’s MCP server |
| Set up | Install the packages, and pin Firenook in package.json with an emulators script |
↑ and ↓ move between the rows, ←, → and Space change one, and the line
under the row you are on says what it does: for TypeScript,
functions/src/index.ts, compiled to lib/ as you save. Esc or Ctrl-C leaves
with Cancelled: nothing was written. and exit code 130, and the folder stays
as it was.
When the folder sits inside another package, a package.json in a folder
above it, Pin Firenook starts off and its line names that file: add Firenook
there instead.
What Enter does
Section titled “What Enter does”Enter writes the files, gives the chosen agents the MCP server, pins
Firenook, and runs the installs: npm install in functions/, and your
package manager’s install in the project. A TypeScript codebase is then built
once. Each step gets a line with its time:
├─ Set-up ──────────────────────────────────────────────────────── 15.3 s ─┤ │ │ │ Files ✓ 4 ms firebase.json, .firebaserc, rules, functions/, … │ │ Agents ✓ 0 ms .mcp.json for Claude Code │ │ Firenook ✓ 517 ms 0.2.0 in package.json │ │ Packages ✓ 14.1 s npm install in functions/, 241 packages │ │ Build ✓ 1.1 s tsc, into functions/lib │ │ │Then init asks Start the emulators now?. Yes starts them in the same
terminal. No ends with what to run later:
├─ Next ───────────────────────────────────────────────────────────────────┤ │ │ │ Start npm run emulators │ │ starts the emulators and compiles your TypeScript as you │ │ save │ │ Connect the console's Connect button has the lines that point │ │ your app at the emulators, for each SDK │ │ │ ╰──────────────────────────────────────────────────────────────────────────╯Ctrl-C during the installs stops them and keeps the files already written.
On a Node older than 22, init says how to switch instead of offering to
start.
Start it
Section titled “Start it”One command starts the emulators from then on:
npm run emulatorsThe emulators script is firenook emulators:start --keep-data --build:
--keep-datakeeps your data between runs in.firenook/state, from the first start. Without it every start begins empty, as the official emulator does. See keep data between runs.--buildruns the codebase’s ownbuild:watchscript (tsc --watch) beside the emulators. Your TypeScript is compiled before it loads and again on every save, with any errors in the log. See TypeScript.
The codebase has one HTTP function. Call it once the emulators are ready:
$ curl http://127.0.0.1:5001/demo-my-app/us-central1/helloWorldHello from Firebase!The console is at http://127.0.0.1:4000/console.
The codebase has a serve script of its own, as the official template does.
From functions/, npm run serve starts only the Functions emulator, with
--build for TypeScript; Firenook finds firebase.json in the folder above.
What init writes
Section titled “What init writes”| File | What it holds |
|---|---|
firebase.json |
Firestore with its rules and indexes, Storage with its rules, the Functions codebase in functions/ with a predeploy build, every emulator on its official port, and the console on |
.firebaserc |
The project id, as projects.default |
firestore.rules |
The official starter rules: open to every client for 30 days from today, then closed |
firestore.indexes.json |
No indexes and no field overrides |
storage.rules |
The official starter rules: every client read and write is denied |
functions/package.json |
firebase-functions 7 and firebase-admin 13 on Node 24, TypeScript 7, and the build, build:watch and serve scripts |
functions/tsconfig.json |
The official settings: src/ compiled into lib/ |
functions/src/index.ts |
The helloWorld HTTP function |
functions/.gitignore |
node_modules/, lib/ and *.local |
.gitignore |
.firenook/ and *-debug.log, appended when missing |
package.json |
With Pin Firenook: Firenook at an exact version, and the emulators script |
.mcp.json, .cursor/mcp.json, .vscode/mcp.json |
Firenook’s MCP server, for each agent you chose |
These are the official firebase init templates. With JavaScript,
functions/index.js holds the function instead, with no tsconfig.json, no
build scripts and no predeploy, and the scripts start without --build.
The scripts, as init writes them for TypeScript:
{ "name": "my-app", "private": true, "scripts": { "emulators": "firenook emulators:start --keep-data --build" }, "devDependencies": { "firenook": "0.2.0" }}"scripts": { "build": "tsc", "build:watch": "tsc --watch", "serve": "firenook emulators:start --only functions --build"},The Admin SDK and server code are not bound by the rules. Your app’s client
SDKs are, so write real rules before the Firestore starter rules close. The
emulators and their ports, from the firebase.json that init writes:
{ "emulators": { "auth": { "port": 9099 }, "functions": { "port": 5001 }, "firestore": { "port": 8080 }, "pubsub": { "port": 8085 }, "storage": { "port": 9199 }, "eventarc": { "port": 9299 }, "tasks": { "port": 9499 }, "hub": { "port": 4400 }, "logging": { "port": 4500 }, "ui": { "enabled": true, "port": 4000 }, "singleProjectMode": true }}Change a port here and Firenook listens on the new one; your app’s connect calls must use the same port. The ports reference lists them all.
Without a terminal
Section titled “Without a terminal”In a script, in CI, or with its input redirected, init asks nothing. It
writes the files, installs nothing, and prints the commands that finish the
set-up:
$ npx firenook initFirenook init: project demo-my-app in ~/my-app wrote ~/my-app/firebase.json wrote ~/my-app/.firebaserc wrote ~/my-app/firestore.rules wrote ~/my-app/firestore.indexes.json wrote ~/my-app/storage.rules wrote ~/my-app/functions/package.json wrote ~/my-app/functions/tsconfig.json wrote ~/my-app/functions/src/index.ts wrote ~/my-app/functions/.gitignore wrote ~/my-app/.gitignoreNext steps: npm install --prefix functions npx firenook emulators:start --keep-data --build npx firenook mcp add (gives Claude Code the Firenook MCP server)The last line appears when the folder shows a coding agent. --yes takes the
screen’s choices without asking and does the set-up too, a line a step:
$ npx firenook init --yesFirenook init: project demo-my-app in ~/my-app wrote ~/my-app/firebase.json wrote ~/my-app/.firebaserc wrote ~/my-app/firestore.rules wrote ~/my-app/firestore.indexes.json wrote ~/my-app/storage.rules wrote ~/my-app/functions/package.json wrote ~/my-app/functions/tsconfig.json wrote ~/my-app/functions/src/index.ts wrote ~/my-app/functions/.gitignore wrote ~/my-app/.gitignore done Agents: .mcp.json for Claude Code (0.0 s) done Firenook: 0.2.0 in package.json (0.4 s) done Packages: npm install in functions/, 241 packages (2.3 s) done Build: tsc, into functions/lib (0.8 s)Next steps: npm run emulatorsThese flags change the choices, in a terminal or not:
| Flag | Does |
|---|---|
--yes |
Takes the choices without asking, and installs, pins and builds |
--no-install |
Installs nothing; the next steps list the installs |
--no-pin |
Leaves package.json as it is |
--language typescript |
The Functions codebase: typescript (the default), javascript or none. none writes no codebase, and so no Functions, Cloud Tasks or Eventarc emulator (--no-functions does the same) |
--only firestore,auth,storage,functions,pubsub |
The emulators to set up. The console and the hub always come; Cloud Tasks and Eventarc come with functions |
--agent claude,cursor,vscode |
The coding agents to give the MCP server, instead of the ones the folder shows |
--project ID |
Uses this project id instead of demo-<folder> |
--dry-run |
Prints each file as write or keep, and writes nothing |
--force |
Writes firebase.json over an existing one, and keeps every other file that exists |
--json |
Prints the plan and the next steps as one line of JSON, and asks nothing |
The demo- project id
Section titled “The demo- project id”init names the project after its folder: demo- plus the folder’s name in
lowercase, with other characters turned into hyphens. A folder called
My Shop gets demo-my-shop, and a folder already called demo-shop keeps
that name.
A project id that starts with demo- is local only, as it is for the official
emulator. Firenook contacts no cloud service for it, and you need no Google
account or login. Your app must use the same id when it connects; see
connect your app.
Your Functions are still your code, and a function that calls an outside API reaches it. The plain output that pipes and CI get says so as it starts:
Demo project demo-my-app: no cloud service is contacted, but user Functions can still reach external providers; this CLI is not a network sandbox.If .firebaserc already names a project, init keeps it. --project picks
another: any demo- id, or a real project id (6 to 30 lowercase letters,
digits and hyphens, starting with a letter).
A folder that has a firebase.json
Section titled “A folder that has a firebase.json”In a terminal, init in a folder with a firebase.json shows what
init --adopt would add, and adds it on a yes; see
switch from the Firebase emulators. Without a
terminal it stops and says what to do:
$ npx firenook initFirenook: ~/demo-shop/firebase.json exists; use firenook init --adopt to adapt it, or --force to write a new one (the other files there are kept)--force writes firebase.json again and keeps the rules, the indexes, the
Functions code and the rest of .firebaserc as they are. Delete a file first
to get its template. TypeScript is the default there too, so pass
--language javascript for a JavaScript codebase.
Connect your app to the emulators, then run your tests against them.