# 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.

Page: https://sdk.chatfuel.com/docs/guides/upgrade
Markdown: https://sdk.chatfuel.com/docs/guides/upgrade.md

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 [#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 SDK                       | In your app             | You import it as |
| -------------------------------- | ----------------------- | ---------------- |
| `packages/ui/src`                | `src/vendor/ui`         | `~ui`            |
| `packages/api-client/src`        | `src/vendor/api`        | `~api`           |
| `packages/vite-plugin-proxy/src` | `vendor/chatfuel-proxy` | relative 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.

<Steps>
  <Step>
    ### Read which version installed you [#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:

    ```ts title="packages/wizard/src/scaffold/skills.ts"
    interface SkillSidecar {
      module: string;
      wizardVersion: string;
      installedAt: string;
    }
    ```

    ```ts title="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:

    <Tabs items="['Claude Code', 'Codex CLI']">
      <Tab value="Claude Code">
        ```bash
        cat .claude/skills/chatfuel-bookings/chatfuel.skill.json
        ```
      </Tab>

      <Tab value="Codex CLI">
        ```bash
        cat .agents/skills/chatfuel-bookings/chatfuel.skill.json
        ```
      </Tab>
    </Tabs>

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

    ```bash
    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.
  </Step>

  <Step>
    ### Scaffold the new version beside yours [#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:

    ```bash
    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.
  </Step>

  <Step>
    ### Diff the three trees [#diff-the-three-trees]

    ```bash
    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:

    ```bash
    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.
  </Step>

  <Step>
    ### Read the changelog for what a diff cannot apply [#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.
  </Step>

  <Step>
    ### Take the new skills [#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.
  </Step>

  <Step>
    ### Verify [#verify]

    From the app directory:

    ```bash
    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:

    ```bash
    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.
  </Step>
</Steps>

## What to read next [#what-to-read-next]

[Vendoring](/docs/concepts/vendoring) is the decision this page is the consequence of.
[Agent skills](/docs/concepts/agent-skills) covers what the skill directories hold and why they
are versioned separately from the code. The [CLI reference](/docs/reference/cli) lists every flag
the scratch run above uses.
