Skip to content
ChatfuelSDK
Guides

Take a newer version of the SDK

The design system, the API client and the proxy live in your repository as source, so an upgrade is a diff rather than an install. Scaffold the new version beside yours, compare three directories, and finish on npm run check && npm test.

There is no npm update to run. Nothing in your package.json points back at this repository — the design system, the API client and the proxy were copied into your project as source, and the CLI that put them there was gone the moment it finished. That is what stops anything we publish from breaking your app, and it is also the cost: an upgrade is a diff you look at and apply, not a version number you bump.

Two ways to get that diff. Scaffold the new version into a scratch directory and compare the trees, which is the one below. Or read the changelog and hand-apply what you want — reasonable when you have edited the vendored code heavily and a mechanical diff would be mostly your own changes coming back at you. CHANGELOG.md ships inside the published package, so it travels with every npx run.

What is yours and what is ours

Three directories in your app are copies of directories in the SDK repository. Every file in them was copied unchanged; the only thing left behind is the packages' own unit tests, which would otherwise hand you failing vitest runs for code you did not write.

In the SDKIn your appYou import it as
packages/ui/srcsrc/vendor/ui~ui
packages/api-client/srcsrc/vendor/api~api
packages/vite-plugin-proxy/srcvendor/chatfuel-proxyrelative paths

Everything else in the app is either yours or generated. Your module code lives in src/modules/<id>/. The shell around it — App.tsx, ShellHost.tsx, Switcher.tsx, BrandMark.tsx, client.ts, useWorkspaces.ts, index.css, src/lib/, and the vite.config.ts, server/, api/ and Dockerfile at the root — is a template you were handed a copy of and may have edited since. Two files under src/modules/ are written by the wizard rather than by a person: index.ts, the registry, and navGroups.tsx, the menu, which the wizard filters down to the modules you picked.

That split is the whole reason the upgrade is tractable. The vendored trees you can replace wholesale if you never touched them. The shell files you read a diff of. Your modules you leave alone.

Read which version installed you

The app records no version of its own, but each installed skill does. The wizard writes a sidecar next to every skill directory it copies:

packages/wizard/src/scaffold/skills.ts
interface SkillSidecar {
  module: string;
  wizardVersion: string;
  installedAt: string;
}
packages/wizard/src/scaffold/skills.ts
const sidecar: SkillSidecar = {
  module: moduleId,
  wizardVersion: WIZARD_VERSION,
  installedAt: new Date().toISOString(),
};

Each module's skill installs as chatfuel-<id>, so read any one of them:

cat .claude/skills/chatfuel-bookings/chatfuel.skill.json

If you chose a global skills install they are under ~/.claude/skills/ or ~/.agents/skills/ instead. Compare what you read against what is published:

npx @chatfuel/wizard@latest --version

The sidecar is also what makes a re-run safe rather than destructive: when a skill directory is already there, the wizard reads the sidecar, tells you which version put it there and when, and replaces it only if you say yes.

Scaffold the new version beside yours

The wizard refuses a directory that exists and is not empty, so it cannot overwrite your app even by accident. Point it somewhere new:

CHATFUEL_TOKEN=<your token> npx @chatfuel/wizard@latest \
  --yes --dry-run \
  --dir ../chatfuel-next \
  --modules bookings,contacts,livechat

Pass the same --modules list your app has — ls src/modules is that list. --yes answers every question, which also means no deploy, no GitHub push, no agent handoff and no dev server at the end. A run with no questions cannot ask for a token either, so it needs a working CHATFUEL_TOKEN in the environment and says CHATFUEL_TOKEN is not set rather than opening a prompt nobody can answer. --dry-run stops it before it creates anything on your Chatfuel or Supabase account. It does still install the scratch app's dependencies, which is the minute this step costs.

The three vendored trees do not depend on which modules you picked, so if all you want is a design-system diff, one small module in the list is enough.

Diff the three trees

diff -ru chatfuel-app/src/vendor/ui        chatfuel-next/src/vendor/ui
diff -ru chatfuel-app/src/vendor/api       chatfuel-next/src/vendor/api
diff -ru chatfuel-app/vendor/chatfuel-proxy chatfuel-next/vendor/chatfuel-proxy

An empty diff means that tree is current. A diff that is entirely ours — you never edited these files — is a directory you can replace wholesale. A diff with your edits mixed into it is the case the vendoring was for: you own those files, and which side of each hunk wins is your decision, not ours.

Then the two generated files, which are comparable because a person did not write either:

diff -u chatfuel-app/src/modules/index.ts       chatfuel-next/src/modules/index.ts
diff -u chatfuel-app/src/modules/navGroups.tsx  chatfuel-next/src/modules/navGroups.tsx

And the shell, where the diff is a reading exercise rather than a copy: src/App.tsx, src/ShellHost.tsx, src/lib/, vite.config.ts, server/, api/ and package.json.

Leave src/modules/<id>/ out of it for every module you have edited. That code is yours, and a newer version of it is not an upgrade — it is somebody else's app.

Read the changelog for what a diff cannot apply

A file diff shows a renamed environment variable but not that your Supabase project needs a migration, and not that saved links now point at addresses the app no longer serves. The changelog's Breaking section is where those live. What one has looked like in practice, 0.2.0:

The wizard asks for a workspace, not a bot. An app scaffolded by 0.1.0 was pinned to the one bot id chosen at setup. The change is in the shell and the environment — VITE_CHATFUEL_WORKSPACE_ID instead of a bot id, and a bot picker in the top bar — so applying it by hand means taking the new src/App.tsx, src/Switcher.tsx and src/useWorkspaces.ts, then setting the new variable. Taking the shell diff and leaving the variable is the failure mode: the app builds and opens on nothing.

The scaffolded app has real URLs. /livechat, not #/livechat. This one comes free with the shell diff — src/lib/route.ts rewrites an old address the first time it is opened, so links in invite mail and bookmarks keep working — but every host has to serve the SPA fallback, which is a change in vite.config.ts, server/entry.ts, the Dockerfile and vercel.json rather than in your module code.

auth: an account is no longer one bot. This is the one no diff can do for you. The app half arrives with the shell files; the data half is a migration you run on your own Supabase project, supabase/migrations/0002_chatfuel_multi_bot.sql, after 0001. The wizard applies it when it has a Supabase access token; otherwise you paste it into the SQL editor. Every migration in the scaffold is idempotent and applied in name order, so re-running one over a project that already has it is safe.

The habit worth keeping: read the Breaking section first, decide what you are taking, then diff. Diffing first tells you what changed and never why.

Take the new skills

The skills are what your coding agent reads, and they go stale in a way nothing in the app does — a skill describing a route that moved is worse than no skill. Re-running the wizard reinstalls them: an existing directory is replaced only after it tells you which version is there and you confirm.

If you switch coding agents at the same time, the layouts differ and neither CLI reads the other's directory — Claude Code loads .claude/skills/ and CLAUDE.md, Codex loads .agents/skills/ and AGENTS.md. A single run that starts with neither CLI on PATH writes Claude's layout for want of a better guess, and relayoutSkills moves the installed directories over if you then accept the Codex install at the handoff. It moves them rather than copying, and takes the vacated directories with it only while they are empty — a global install shares ~/.claude with everything else you keep there. Pass --agent claude or --agent codex to settle it up front instead.

Verify

From the app directory:

npm run check && npm test

check is tsc --noEmit over the app and a second pass over the scripts; test is the suite that ships with the app, including the render smoke test every module carries. A vendored tree that half-landed shows up here as a type error, and a shell file taken without the file it now imports shows up as a module that renders nothing.

Then start it and click into each module:

npm run dev

npx @chatfuel/wizard doctor is the other half of the check — it reports Node, the package manager, the coding agent and the bundled content, and it is the fastest way to find out that the CLI you ran is not the one you thought you ran.

Vendoring is the decision this page is the consequence of. Agent skills covers what the skill directories hold and why they are versioned separately from the code. The CLI reference lists every flag the scratch run above uses.

On this page