Run it in CI, or without a terminal
--yes answers every question the wizard asks except two, which have to be in the environment — and it refuses two steps outright.
Every question the wizard asks has a non-interactive answer, and --yes takes it. Two answers are
the exception, because they are credentials and a command line is not the place for them: they
come from the environment or the run stops.
Stopping is the design. The prompt library exits the process on end-of-input, so a scripted run
that fell through to a question nobody could answer would report success and leave nothing behind.
A wizard that says CHATFUEL_TOKEN is not set and exits 1 is the version you can put in a
pipeline.
Ask the machine what it can see
npx @chatfuel/wizard doctorOne row each: Node against the floor, the outbound proxy it will use, which coding agents are on
PATH, whether the bundled content tree is complete, and one line per module skill. A missing
agent is a warning; anything else fails and the command exits 1.
Run it as the first job of the pipeline. It asks nothing and reaches nothing, so a red row here is a machine problem rather than a credentials problem, and you find out before spending a token check on it.
Put the two credentials in the environment
| Name | What needs it |
|---|---|
CHATFUEL_TOKEN | Always. There is no flag for it. Generate one at the token page. |
SUPABASE_ACCESS_TOKEN | Only with the auth module, and only if you are not passing --supabase-token. |
The token is used on the first attempt in place of the prompt, and then checked against the API.
Under --yes there is no second attempt: a token that is missing, or one the API rejects, ends
the run with a sentence naming which of the two happened.
ADMIN_PASSWORD is read the same way if you install the admin module, but it is not part of
this contract — a run that finds neither the flag nor the variable generates a strong password,
writes it to .env and prints it once.
Answer the rest with flags
--yes alone gives you a standalone app in ./chatfuel-app with every ready module except the
opt-in ones, skills in the project, and the Chatfuel workspace picked for you. The flags that are
worth setting anyway:
| Flag | Why in CI |
|---|---|
--dir <path> | The default is ./chatfuel-app relative to where you started. A directory that exists and is not empty is refused outright. |
--modules <ids> | --yes skips opt-in modules, so auth and admin arrive only if you name them. An unknown id ends the run and lists the ready ones. |
--workspace <id> | Taken silently when the account owns exactly one. With several and the auth module, --yes refuses — which workspace pays is not a question to answer on somebody's behalf. |
--agent <id> | With both CLIs on PATH, --yes takes the first one it detected instead of asking. Name the one whose skills layout you want. |
--app-name / --logo | The name otherwise falls back to the workspace's title; the mark otherwise stays the shipped one, because --yes never asks for a logo. |
--embed is the only thing that chooses embed mode — --yes on its own stays standalone. The
full table, including what each flag falls back to, is the CLI reference.
Some things --yes does without asking, worth knowing before you point it at a machine you care
about: it adds .env to .gitignore, replaces an existing skill directory, installs no coding
agent when none is present, and starts no dev server at the end.
Name the Supabase project up front
The auth module is the one module with a second system behind it, and under --yes it needs to
be told which project — a token on its own does not say. There are exactly two accepted shapes:
SUPABASE_ACCESS_TOKEN (or --supabase-token) plus one of:
--supabase-project <ref> # a project that already exists
--supabase-create <name> # make one, or reuse the one already called that--supabase-create reusing by name is what keeps a repeated pipeline from spending the account's
other free project on a duplicate. Two projects with that name is an error naming both refs rather
than a guess.
Add --supabase-org <slug> when the token sees more than one organization — under --yes that is
otherwise a refusal that lists the slugs. --supabase-region defaults to the recommended one.
Neither shape present and the run stops before the scaffold writes anything, with a hint naming
every flag that would have worked. A --yes run also never creates a project it was not explicitly told to create — that
is a second, deliberate backstop behind the same rule.
Get the calls out of the network
Node's own fetch ignores HTTP_PROXY and HTTPS_PROXY unless the process was started with a
flag nobody types, so the wizard reads them itself — https_proxy, HTTPS_PROXY, http_proxy,
HTTP_PROXY, all_proxy, ALL_PROXY, honouring NO_PROXY, with loopback always exempt.
export HTTPS_PROXY=http://proxy.example:8080What has to be reachable, and when:
| Host | For |
|---|---|
panel.chatfuel.com | Always. |
registry.npmjs.org | Installing anything. |
api.supabase.com | The auth module's access-token path. |
api.vercel.com | Deploying. |
api.github.com | Pushing to GitHub. |
doctor prints the proxy it resolved on its own row, which is the cheapest way to find out that
the variable is spelled differently than you think.
Rehearse with --dry-run
--dry-run stops account assets, not files. The app is still written to disk and its dependencies
still installed; what it holds back are the calls that change something on an account — the
Chatfuel trial checkout, the Supabase project creation, the database migrations and the auth-config
patches. Each is printed as the call it would have made. A project it would have created does not
exist, so there are no keys to read and .env carries empty Supabase values.
So a dry run tells you your flags parse, your tokens work, and your directory is writable, for the price of a scaffold you delete afterwards.
One Chatfuel write has no --dry-run branch. If the token's account owns no workspace at
all, the workspace step creates one before anything else can continue — under --dry-run as
well. On an account that already has a workspace, nothing is created.
Run it, and check what came out
CHATFUEL_TOKEN="$CHATFUEL_TOKEN" \
SUPABASE_ACCESS_TOKEN="$SUPABASE_ACCESS_TOKEN" \
npx @chatfuel/wizard \
--yes \
--dir ./chatfuel-app \
--modules auth,livechat,contacts,deals \
--workspace 8f3c1d2e-4a5b-6c7d-8e9f-0a1b2c3d4e5f \
--agent claude \
--app-name "Acme Concierge" \
--logo ./brand/acme.svg \
--supabase-create acme-concierge \
--app-url https://app.acme.comA failed run exits 1 with the wizard's own sentence and its hint, so the pipeline step goes red on its own. A successful one leaves an app with its dependencies installed:
cd chatfuel-app
npm run checktsc passing over the whole app is the check that means the registry, the modules and the
vendored trees all agree. Then read .env: every variable the run resolved is in it, and it is the
file the deployment's configuration is pushed from.
Two things will not be there, on purpose. No deployment, and no GitHub repository: both
steps are interactive-only and skipped under --yes, without a TTY, and under --dry-run. A
deployment is a public URL with your Chatfuel token behind it, and a repository under your account
holds the source of an app wired to your bot; neither is something a flag should be able to cause.
Both are ordinary commands afterwards — npm run deploy and gh repo create --source . --push.
Next: deploy for the step --yes would not take, or
the CLI reference for every flag and its fallback.
Replace the Chatfuel token
npx @chatfuel/wizard auth rewrites one line of one file. The rest of the job is getting that value to wherever the proxy actually reads it.
Put it in a repository
The app directory holds a live Chatfuel token, so the first push is a secret question before it is a git question. What the wizard's GitHub step checks, how to do it by hand, and how to make git push the deploy command.