vendo bin ships in @vendoai/vendo, so installing the package is all it takes. Before anything is installed, npx vendoai@latest init runs it straight from npm.
One command. It scans the app with deterministic analysis, writes everything it can, and tells you the little that is left.
Init never writes to a file you authored. Every file it creates is new and Vendo-owned, plus its own package.json hooks.
What init writes
It also adds idempotent
predev and prebuild hooks to package.json:
vendo out of node_modules/.bin, so the bin has to be installed in the project — not just run once through npx. @vendoai/vendo carries it, and the files init writes import that package anyway, so a project it wired can always run its own hooks.
The hooks carry --no-ai explicitly, so npm run dev can never stop to ask a question or spend on inference. An existing script keeps its formatting and gets vendo sync prepended.
The one step left
Init writes no client file. Mounting the provider is yours to paste, and init prints the exact file and lines:--agent write run’s receipt, in pasteEdits. Until it lands, vendo doctor fails with E-WIRE-004 and reprints it.
A host with its own components writes one "use client" file holding the component registry and the provider, and mounts that instead. The registry carries component references, which cannot cross a Server Component boundary.
How detection decides
Init makes two independent, dependency-based calls before it writes anything.Framework
Init checks
next first, so an app with both dependencies gets the Next scaffold. A non-interactive run with no detected framework errors and asks for --framework rather than guessing.
The custom scaffold takes a web Request and returns a Response, constructed lazily on the first request. Cloudflare Workers, Bun, Deno, Hono, and Lambda each mount it in one line.
Monorepos: init targets the app package, not the repo root. Detection reads the
package.json of the directory init runs in, or the one you point it at (npx vendo init apps/web). Run login, doctor, and sync from that same directory: login writes the minted key to .env.local in the current working directory.Auth
Init readspackage.json and detects one auth family.
A single detected family gets one
[Y/n] confirm before anything is written. Decline it, or have several or none detected, and init leaves principal unwired and prints the exact one-line auth: addition.
--auth <preset> answers without the prompt. The presets and when to pick each are on Auth.
The model key
Init runs a key step rather than scaffolding a model file. If a provider key is already set it says so; otherwise it offers a Vendo Cloud key inline.vendo login approves a code in the browser and appends the minted VENDO_API_KEY to .env.local without touching your other keys. You never paste a key.
Init never fails on a Cloud error. vendo cloud login <email> is the email-OTP fallback.
What an interactive run asks
There is no interview. An interactive run stops only for Enter-to-accept decisions:- the detected auth preset
- where the app runs in dev, prefilled from your
devscript’s port — Enter accepts it, and the answer lands in.env.localasVENDO_BASE_URL - the Vendo Cloud key offer
- consent for the AI judgment pass
- one aggregated review, if that pass proposes a loosening
- a
zodbump when your pin is below 3.25 - an uncertain theme slot
- whether your own backend will call tools machine-to-machine (
--use-case mcp) - whether to run
vendo doctoron the way out
--yes answers all of them and ends with an Agent tail: block naming the files left to hand-edit.
Agent mode
--agent changes how init’s questions travel, not what init asks. Called with no answers, it detects what it can and prints ONE JSON object holding the questions it still needs answered. It writes nothing and exits 0.
Each question carries its options, the recommended pick, and the flag that answers it. Your coding agent relays them to you in chat, then re-runs init with your answers as flags:
status field tells the two runs apart, so an agent never has to guess whether the repo was touched.
The answer flags are the ones init already has:
--use-case, --auth, --cloud-key or --byo, and --base-url. Nothing else is asked. The zod bump, uncertain theme slots, and the closing live check never surface in agent mode — they take exactly the defaults --yes takes.
Every flag
vendo init [dir]. dir is the app to initialize and defaults to the current directory.
--cloud-key and --byo answer the same question, as do --ai and --no-ai and --check and --no-check. Passing both halves of a pair exits 1.
--posture or --service-key without --use-case mcp exits 1 rather than being silently ignored, and so does --service-key with --posture broker: a Cloud-fronted door provisions its own key, so accepting one there would discard it. --ai-polish is the older spelling of --ai.
--posture broker exits 1 too when the dev URL is not https://: Vendo Cloud registers that origin as the tenant’s forwarding address and refuses a non-https one, so the door would be written and then fail on its first request. Take --posture local — zero config, works on http — or pass an https origin. The check reads the URL the run resolved, not the flags, so it catches a broker posture picked at the prompt too.
Init rejects an option it does not recognize and exits 1 before doing anything. A flag an older CLI does not support can never be silently dropped.
Re-running init
Init only creates what is missing. A rerun on a wired app prints “Already wired” and still re-runs extraction, the theme reconcile, and the AI-pass offer. Day to day you do not re-run init at all. Thepredev and prebuild hooks call vendo sync on every build, which is what keeps .vendo/tools.json current.
Re-run init when the detected surface changes in a way sync cannot scaffold for, such as adding Express to a Next app. Starting over or removing Vendo is on Uninstall.
Verify
vendo doctor owns verification.
.vendo/ files, the tool catalog, the model credential in your environment — and needs no running app. --json is the machine-readable gate agents loop on. Every check and flag.
Right after a release, package managers with a cooldown hold the new version back. pnpm 11 fails loud with “no versions available”; npm’s opt-in
min-release-age instead resolves silently to an older version, which init then pins.Check what you got with npm ls @vendoai/vendo. To take the current release anyway, pass --min-release-age=0 on npm, or exclude Vendo in pnpm-workspace.yaml: