# Moving a site to site-kit 7

site-kit 7 is organised the way Sonor's dashboard is: one entry per module.
Most sites need no changes at all. Move a site when you next touch it;
nothing forces it before then, because a
site's `^6` range never picks up 7.

## The short version

```bash
pnpm add @sonordev/site-kit@^7
npx sonor-setup codemod --check      # lists what needs moving (writes nothing)
npx sonor-setup codemod --write      # moves it
pnpm build                           # a bump you didn't build is a guess
```

Run the codemod in each app or workspace package that imports site-kit (in a
monorepo, inside each workspace package too). It's
idempotent: running it twice changes nothing the second time. It works from
any 2.x-6.x site: copies of sites on 4.2, 5.8 and 6.5 were migrated with it
and built.

It leaves nothing in the project but the change. Since sonor-setup 7.1.2 the
originals of the files it writes go to a folder in your OS temp directory
(it prints where, and git has them too), not `.bak` files beside them, and
it never creates or edits `.env.example`: `.env.local` is the one env file.
An old fallback such as `process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY`
becomes `process.env.SONOR_API_KEY`.

Requirements: Next 16 and Node 20.19+ or 22.12+
(Netlify's default is 22).

## What changed, and what the codemod does about it

### 1. The root entry is types-only

`@sonordev/site-kit` exports types and `SITE_KIT_VERSION`, nothing that runs.
A stray root import used to be able to pull commerce, signal or redirect code
into a page's bundle.

| Was                                                                    | Now                                                       |
| ---------------------------------------------------------------------- | --------------------------------------------------------- |
| `import { BookingWidget } from '@sonordev/site-kit'`                   | `@sonordev/site-kit/sync`                                 |
| `import { AffiliatesWidget, useAffiliates } from '@sonordev/site-kit'` | `@sonordev/site-kit/affiliates` (new entry)               |
| commerce components and fetchers                                       | `@sonordev/site-kit/commerce`                             |
| `ManagedImage` and the image helpers                                   | `@sonordev/site-kit/website/images`                       |
| `SignalBridge`, experiments, `useSignal*`                              | `@sonordev/site-kit/signal`                               |
| `TestimonialSection`, `fetchReviews`                                   | `@sonordev/site-kit/reputation`                           |
| `handleManagedRedirects` and friends                                   | `@sonordev/site-kit/seo/redirects`                        |
| `LandingPage`, `landingPageMetadata`                                   | `@sonordev/site-kit/website/landing`                      |
| `formatBookingTime`, `formatBookingDate`                               | `formatTime`, `formatDate` from `@sonordev/site-kit/sync` |

**The codemod rewrites these imports**, including the two renamed ones, and
keeps type imports on the root. It flags, rather than rewrites, a namespace
import (`import * as SK`), a `require`, or a re-export from the root. The full
list is `src/shared/module-map.json` (`rootRuntime`).

### 2. The setup CLI is its own package: `sonor-setup`

The CLI (init, scaffold, codemods, OG cards, GEO wiring, verify) was 3 MB of
every site's install and forced a site-kit release for every setup-only fix.
It's now [`sonor-setup`](https://sonor.dev/cli).

- **`npx sonor-setup …` keeps working unchanged:** npx fetches the package.
- **A site whose package.json scripts run `sonor-setup`** (an OG step that
  runs `sonor-setup og`, say) needs it as a dev dependency. The codemod adds
  `"sonor-setup": "^7.0.0"` for you.
- **`sonor-register-sitemap` stays in site-kit**, since sites run it from
  their postbuild. Nothing to change.
- The `site-kit` bin alias for the CLI is gone (no site used it).

### 3. One entry per Sonor module (old paths still work)

New homes, each the same module as before:

- Website: `./website/{popups,images,slots,cms,landing,cta-bar}`
- SEO: `./seo/{sitemap,robots,indexnow,redirects,og,og/route,llms,llms/client,llms/contract,meta/contract,pages/contract}`
- Website chat: `./chat`, and popups: `./website/popups` (both out of
  `./engage`, which Sonor retired with the Engage module)

**The old paths keep working through 7.x** and are removed in 8.0. The
codemod moves them anyway, so a touched site is done in one pass: chat
imports go to `./chat`, popup types to `./website/popups` under their new
names (`EngageElement` is `SitePopup`). It flags `EngageWidget`, which draws
both and which `SiteKitLayout` already mounts. `SiteKitLayout` gains `chat`
and `popups` props; `engage` is an alias (`engage={false}` still turns both
off). Since 7.2.0, popups render only from blocks: one made in Engage Studio
isn't drawn, so make it again in Sonor (Website → Popups & Banners).

### 4. ESM only, Next 16 only

site-kit ships ES modules alone (`"type": "module"`). Next, Turbopack and the
kits don't notice. Node 20.19+/22.12+ can `require()` it too, so a
`next.config.ts` that imports `@sonordev/site-kit/config` keeps working. A
site-side script that `require()`s site-kit on an older Node would break.

The `next` peer is `^16`. Next 16 renamed `middleware.ts` to `proxy.ts`, and
**the codemod moves it**: the file, a named `middleware` export (to `proxy`),
and `createMiddleware` from `@sonordev/site-kit/middleware` (to `createProxy`
from `/proxy`). The old names work through 7.x.

**The proxy goes beside the app directory.** Next reads it from the project
root, or from `src/` when the app is `src/app`, and silently ignores one
anywhere else: the build passes, but the site sends no security headers and
runs no managed redirects. So on a `src/app` site the codemod writes
`src/proxy.ts`, with the file's relative imports re-pointed. Before
sonor-setup 7.1.2 it renamed the file in place, which left a root `proxy.ts`
on those sites; rerunning the codemod moves it, and `npx sonor-setup doctor`
warns about any proxy Next doesn't read.

It won't move a file that sets `runtime` (a proxy file can't; the build
throws), and says so: remove the export, check the logic still runs on your
host, rerun. It never moves onto a proxy Next already runs: with one in each
place, it reports both and changes neither. `npx sonor-setup next16` runs
just this part.

### 5. Removed (nothing used them)

- `@sonordev/site-kit/search` and `/search/contract`
- `@sonordev/site-kit/redirects/not-found` (`resolveManagedRedirect`), and
  with it the `x-sk-path` request header the proxy stamped for it and the
  `SK_PATH_HEADER` export. Managed redirects run in the proxy.
- The postinstall GEO bootstrap (`SITE_KIT_AUTO_GEO=1`). Run
  `npx sonor-setup geo` instead. site-kit no longer runs anything at install.
- `@sonordev/site-kit/setup` (an empty stub since 2.0), `LocationPageContent`
  and `getLocationSection` (they sent a `project_id`), `identifyTopicClusters`
  (use `getTopicCluster`), `formatContentSignals`, and the `SiteKitConfig`
  type (not `withSiteKitConfig`, which stays).
- Source maps. The package no longer ships `.map` files (7.3 of 6.x's 11 MB).

Still accepted and ignored until 8.0, because sites pass them:
`contentSignals` on `createRobotsTxtHandler`, `nativeReturnTo` on forms, and
`<ManagedScripts />`.

### 6. Behavior worth knowing

- **react-markdown is no longer installed with site-kit.** The chat widget
  renders markdown with `marked`, already a dependency. A site that imports
  `react-markdown` itself without listing it (it only worked because npm
  hoisted site-kit's copy) must add it.
- **`sonor-register-sitemap` reads .env files the way Next does**: a variable
  the host set wins over every file, then `.env.production.local`,
  `.env.local`, `.env.production`, `.env`. It used to let `.env.local` beat
  the host.

## Optional, and new in 7.x

- **Cache Components.** `cacheComponents: true` in next.config works with
  site-kit now (it failed every page through 6.x). Remove route segment
  configs (`dynamic`, `revalidate`) when you turn it on; Next rejects them.
- **Agent tools.** `npx sonor-setup mcp` gives the site an MCP endpoint with
  the built-in Sonor tools (business profile, services, FAQ, pages, articles,
  reviews; inquiries, availability and offerings when you ask), the server
  card, and on Netlify the rate-limited relay. Sonor's AI Visibility tab then
  shows which agents used them. A site that already runs its own MCP server
  (a hand-written one) is left exactly as it is:
  nothing in 7.0 turns anything on for it. To have Sonor see its calls, add
  `onToolCall: reportToolCallsToSonor()` to its `createMcpHandler`.
- **`@sonordev/contracts`** is for the APIs and the dashboard; sites keep
  importing `@sonordev/site-kit/*/contract`, which is the same code.
- **Live updates (7.1).** `npx sonor-setup scaffold` writes the route Sonor
  calls when content changes, `app/api/seo-revalidate/route.ts`, and
  `npx sonor-setup doctor` checks for it. A site with articles gets
  `createRevalidateRoute({ publicationBasePath: '/insights' })` with its own
  path, read from the app directory (a dynamic page that renders site-kit
  articles, or a `[slug]` route under a folder like `/blog` or `/news`).
  When more than one folder looks like the publication, it writes the
  one-line route and lists them.

## Kits built on site-kit

agency-site-kit and re-site-kit import only entries 7 keeps unchanged
(`/client`, `/server`, `/llms`, `/forms`, `/mcp`, `/portfolio/contract`),
so their `>=` peer ranges already accept 7.
