@emito/cli
v0.1.0
Published
Installer CLI for Emito — detects your stack, installs the @emito/* packages, migrates the schema, and generates the backend glue files.
Downloads
169
Maintainers
Readme
@emito/cli
The installer CLI for Emito — emito init.
What it is
@emito/cli installs Emito into an existing project. Adopting Emito by hand
means vendoring packages, wiring a JWT auth bridge, mounting a server handler,
writing config, and migrating a schema — this CLI does that work for you and
leaves behind two generated files you own outright.
It ships one binary, emito, with two commands:
| Command | What it does |
| --- | --- |
| emito init | Detects your stack, installs the right @emito/* packages, migrates the schema, and generates emito.config.ts + emito.mount.ts. |
| emito agent-skill | Drops an AI coding agent skill into .claude/skills/emito-init/ so an agent can run the install and wire the mount call into your entrypoint. |
The generated files are yours, shadcn-style — not a hidden library call.
emito initwrites the same hand-assembled scaffold the docs walk you through, then gets out of the way. Edit them freely.
Install
@emito/cli is not yet published to npm. Once it is, the intended usage is:
npx @emito/cli@latest initUntil then, install it from the tarball attached to a GitHub Release — the repo is public, so the URL resolves directly:
pnpm add -D https://github.com/emito-pro/emito/releases/download/v0.1.0/emito-cli-0.1.0.tgz
pnpm exec emito initThe @emito/* packages the CLI then installs have to resolve too, which needs
one overrides block pointing at the same release — the
install page has the copy-pasteable version.
Working from a checkout of the monorepo instead? Build and invoke it directly:
git clone https://github.com/emito-pro/emito.git
cd emito && pnpm install
pnpm --filter @emito/cli build
# then, from your own project:
node /path/to/emito/packages/cli/dist/index.js initemito init
emito initThe run is a wizard, and nothing is written until you confirm the plan:
- Preflight (read-only). Detects your package manager from the lockfile and
your backend framework from
package.json(express,fastify,hono,next, or the generic Node adapter). Checks thatDATABASE_URLandREDIS_URLare set and reachable, prompting for anything missing. Aborts before touching anything if a prerequisite is unreachable, naming which service failed and why. - Plan confirmation. Prints exactly what will be installed and generated.
- Execution. Installs the packages, appends the Emito block to
.env.example, runs the Emito schema migration (in its own migrations table, isolated from yours), and writesemito.config.ts+emito.mount.ts. - Summary. Reports what was generated, what is left to do by hand, and the
freshly generated
EMITO_JWT_SECRETfor you to paste into your real.env.
An existing emito.config.ts or emito.mount.ts aborts the run — the CLI never
silently overwrites files it did not write.
Flags
| Flag | Description |
| --- | --- |
| -y, --yes | Skip every prompt and take the defaults (in-app only, auto-confirm the plan); requires DATABASE_URL and REDIS_URL to already be set. |
| --channels <list> | Comma-separated channels beyond in-app: --channels email,sms. |
| --framework <name> | Backend framework, skipping detection: express, fastify, hono, nextjs, or node. |
emito init --yes --channels email,sms --framework fastifyThe last step is yours
emito init generates the glue but does not edit your entrypoint — it can't
safely infer arbitrary existing code structure. It finishes with
manual-mount-required and tells you the one line to add:
import { buildEmitoServer, mountEmito } from "./emito.mount";
const { server, cleanup } = await buildEmitoServer();
mountEmito(app, server); // signature varies by framework — the generated file shows yoursemito agent-skill
emito agent-skillCopies the bundled skill into .claude/skills/emito-init/SKILL.md. Ask an AI coding agent
to "install Emito" and it runs emito init, then closes the gap the CLI
leaves open: it locates your real entrypoint, inserts the import and
mountEmito call at a safe point, and runs your typecheck as proof it didn't
break anything.
An existing SKILL.md is left untouched — delete it and re-run to take a fresh
copy.
Generated files
| File | Contents |
| --- | --- |
| emito.config.ts | createEmitoRuntime() — the Drizzle repositories bundle, per-channel providers (falling back to mock providers when the API keys are absent), and createEmito(...) with a starter category/event to edit. |
| emito.mount.ts | buildEmitoServer() — createEmitoServer(...) wired to JWT auth, returning { server, emito, repositories, cleanup } — plus a mountEmito(...) using the adapter matching your framework. |
Before production: the generated
resolveWorkspaceRolegrants workspace-admin to every authenticated subscriber. Replace it with real workspace-role resolution — the generated file flags this with aSECURITY TODO.
Part of Emito
@emito/cli is one package in the Emito monorepo —
self-hosted, provider-agnostic notification infrastructure for Node.js and
TypeScript.
- Core engine —
@emito/core - HTTP server & adapters —
@emito/server - Full documentation — docs.emito.io
- See the full package list in the root README.
License
MIT — part of the Emito project.
