CLI Setup Command

avsb init sets up A vs B in the project you are standing in, and finishes by proving it works rather than telling you what to do next.

Shell
npx @avsbhq/cli init
Shell1 line

In one run it:

  1. reads your package.json and works out which framework you use,
  2. links an existing project or creates one,
  3. writes the SDK key into the environment file that framework actually reads,
  4. names the install command for your package manager (or runs it),
  5. writes an example file that reads a real flag,
  6. offers to generate TypeScript types for your flags,
  7. waits for your app's first check-in and prints what it saw.

What it writes, and what it will not touch

avsb init writes at most four files, all inside the directory you point it at:

FileWhen
An environment file (.env.local, .env)The framework has one, and the variable is not already set
.gitignoreInside a git repository, when git does not already ignore that environment file. One line naming the file is added under a comment saying avsb init put it there
An example file (avsb-example.tsx and friends)It does not already exist
Generated flag typesOnly with --codegen, or after saying yes to the prompt

Two rules it never breaks:

  • It never overwrites. A variable already set is left alone, even when it holds a different key, and the run says so. An example file that already exists is left alone too.
  • It never invents a credential. It uses the login avsb login saved on your machine, or the AVSB_TOKEN environment variable.

Running it twice is safe: the second run leaves the environment file byte-identical, lists it under Left alone rather than Written, and simply waits for a check-in again.

Frameworks it knows

Detection reads your dependencies. Pass --framework to override it.

DetectedPackageVariableFile
Next.js@avsbhq/nextAVSB_SDK_KEY.env.local
Nuxt@avsbhq/nuxtNUXT_PUBLIC_AVSB_SDK_KEY.env
SvelteKit@avsbhq/sveltePUBLIC_AVSB_SDK_KEY.env
React, Vue, Solid, Svelte built with Vite@avsbhq/react, @avsbhq/vue, @avsbhq/solid, @avsbhq/svelteVITE_AVSB_SDK_KEY.env.local
React built with Create React App (react-scripts)@avsbhq/reactREACT_APP_AVSB_SDK_KEY.env.local
Vue built with Vue CLI (@vue/cli-service)@avsbhq/vueVUE_APP_AVSB_SDK_KEY.env.local
React, Vue, Solid, Svelte with any other bundleras aboveAVSB_SDK_KEY, to rename.env.local
Angular@avsbhq/angularnonenone
Node.js@avsbhq/nodeAVSB_SDK_KEY.env
No package.jsonnonenonenone

For browser frameworks the bundler decides the variable name, because the bundler decides which variables reach browser code: Vite exposes VITE_ variables, Create React App REACT_APP_, and Vue CLI VUE_APP_. The run says which bundler it found and which name that gave, for example React (found react in package.json, key in REACT_APP_AVSB_SDK_KEY: found react-scripts, and Create React App only exposes REACT_APP_ variables to the browser, installs with npm). With none of the three, avsb init writes AVSB_SDK_KEY and says so in its Next list: rename it to the prefix your bundler exposes, or pass the key to the provider directly.

Angular has no environment-file convention, so the key goes into the example where you bootstrap the client. That is expected: an SDK key is a public identifier, safe to ship in a browser bundle.

A directory with no package.json has nothing to install into, so those steps are skipped and the run says so. What happens next depends on the project, not the directory: a web experiments project is pointed at the snippet tag, and a feature flags project still gets its SDK key, its typed flags, and the wait for the first check-in, because the application it belongs to may well live somewhere else.

Flags

FlagDefaultMeaning
-p, --project <project>promptLink a project: PRJ-42, 42, or a dashboard URL
--create <name>promptCreate a feature flags project with this name
-e, --env <environment>productionWhich environment's SDK key to write
--framework <framework>detectednext, nuxt, angular, svelte, vue, solid, react, node, none
--flag <key>first flag in the projectFlag the example reads
-d, --dir <path>.Project directory to set up
--installoffRun the install command instead of printing it
--no-env-fileoffDo not write an environment file
--no-exampleoffDo not write the example
--example-path <path>framework defaultWhere the example goes
--codegenpromptGenerate flag types as part of setup
--codegen-output <path>./src/generated/flags.tsWhere generated types go
--wait <seconds>120How long to wait for the first check-in
--no-waitoffDo not wait at all
--require-heartbeatoffExit non-zero when no check-in arrives
--no-inputoffNever prompt; use flags and defaults only
--json, --quietoffSee Machine-readable output

Every prompt has a flag, so the command works unattended. With --no-input, --json, no terminal, or CI set, nothing is asked: it takes the documented default, or stops and names the flag it needed.

The proof at the end

Every A vs B SDK reports in after it successfully loads its configuration. That report is what avsb init waits for, and the last line is printed only when two things were actually observed: a check-in newer than this run arrived for that key, and the flag in your example is in the configuration that key serves. A check-in that was already recorded when the wait began does not count, even one from a minute earlier: only one that arrives while the CLI is watching (or in the first few seconds of the wait) proves this setup, so an app that has since stopped cannot pass for a working one.

Plain text
sdk_production_ttqm0eaj4vth1krcb2xn checked in and is serving 'checkout_v2' for Next.js 8s after setup
Plain text1 line

The line says "checked in and is serving" rather than "evaluated" on purpose. The evaluation happens inside your process, where the CLI cannot see it, so the line claims only what was actually observed. The line under it says what the environment serves for that flag when no targeting rule matches.

If nothing arrives before the timeout, everything above it is still written and the run says exactly that, followed by the four things that are usually wrong, most likely first:

  1. Nothing in the environment is turned on yet. A new environment has no configuration for the SDK to load until something in it is published, and the SDK only checks in after it loads one. Turn the flag on with avsb flags toggle <key> --env production --on.
  2. The app never started.
  3. The key in the code is a different one.
  4. Outbound requests to the CDN or the platform are blocked.
Plain text
! No check-in from Production in 120s. Everything above is written; only the proof is missing.  Turn a flag on with `avsb flags toggle checkout_v2 --env production --on`. An environment where nothing has been turned on has no configuration for the SDK to load, and the SDK checks in only after it loads one.  Start your app once, then run `avsb init --wait 120` again to watch for it.
Plain text3 lines

The "Next" list says the same about a flag that exists but is not turned on in the environment you set up, with the exact command.

A timeout is not a failure exit code, because the setup succeeded. Pass --require-heartbeat in CI if you want the run to fail without the proof. It cannot be combined with --no-wait: the two cancel each other out, so the pair is refused as a usage error (exit 2) rather than failing on a wait that never happened.

With --quiet, the proof line is the only thing printed on success. Warnings and the timeout guidance still go to stderr.

In a script

Shell
avsb init --project PRJ-42 --env staging --no-input --no-wait --json
Shell1 line

The JSON envelope carries the project, the environment, the detected framework, every step with the reason it ran or was skipped, the files written, and the check-in result:

Plain text
{  "ok": true,  "command": "init",  "data": {    "project": { "shortId": 42, "name": "Checkout", "type": "FEATURE_FLAG", "created": false },    "environment": { "key": "staging", "name": "Staging", "sdkKey": "sdk_staging_..." },    "framework": { "id": "next", "label": "Next.js", "detectedFrom": "next", "packageManager": "pnpm" },    "flag": { "key": "checkout_v2", "exists": true, "served": { "variationKey": "on", "value": true } },    "steps": [ { "id": "env", "action": "write", "detail": "Write AVSB_SDK_KEY into .env.local." } ],    "files": { "env": "/app/.env.local", "envOutcome": "created", "example": null, "codegen": null },    "install": { "command": "pnpm add @avsbhq/next", "ran": false, "ok": false },    "heartbeat": { "waited": false, "seen": false, "at": null, "sdkType": null, "elapsedMs": 0 },    "proof": null  }}
Plain text15 lines

Teaching your coding agent

Shell
avsb agents-md install
Shell1 line

Writes an A vs B section into CLAUDE.md and AGENTS.md in your repository: the credential vocabulary, the CLI commands, how a flag is read, and the rules that keep an agent from inventing flag keys or putting a token in application code.

The section sits between markers, so re-running the command replaces it instead of adding a second copy, and everything you wrote around it is untouched.

FlagMeaning
-d, --dir <path>Repository directory to write into
--claudeOnly CLAUDE.md
--agentsOnly AGENTS.md
--printPrint the section instead of writing it
-p, --project <project>Project to name in the section
-e, --env <environment>Environment to name in the section
--json, --quietSee Machine-readable output

It makes no network calls and needs no login: the section is built from what is already in your repository.

Was this helpful?