# sb-mig full documentation

# Start Here

Source: /docs

> What sb-mig is, when to use it, and where to go next.
sb-mig is a command line toolkit for Storyblok teams that want schema and content operations to be reviewable, repeatable, and inspectable.

It started as a code-first Storyblok component sync tool. It now covers component schemas, roles, datasources, plugins, stories, assets, backups, content migrations, preset migrations, publication-state inspection, and migration audit artifacts.

## Why teams use it [#why-teams-use-it]

* Keep Storyblok component schemas in code.
* Sync local schema files into Storyblok spaces.
* Move stories and assets between spaces or files.
* Run scripted content migrations over Storyblok story JSON.
* Preserve published and draft layers during sensitive migrations.
* Generate dry-run evidence before writing to Storyblok.
* Find rare component usage patterns across many stories with read-only query files.
* Keep migration output auditable with JSON and JSONL artifacts.

## Current center of gravity [#current-center-of-gravity]

The most important workflow in sb-mig 6.x is safe content migration:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --publicationMode preserve-layers \
  --dry-run \
  --fileName production-check
```

That command previews the selected stories, applies the migration in memory, writes local artifacts, and makes no Storyblok writes.

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

* [Quickstart](/docs/quickstart) for installation and config.
* [Safe content migration](/docs/guides/safe-content-migration) for the production workflow.
* [CLI reference](/docs/reference/cli) for command families.
* [Publication model](/docs/explanation/publication-model) for the reason behind `preserve-layers`.

---

# Architecture

Source: /docs/explanation/architecture

> How the sb-mig CLI, old API layer, and API v2 fit together today.
sb-mig is currently a CLI-first TypeScript package with two API surfaces.

## CLI [#cli]

The CLI entrypoint uses `meow` for parsing and delegates to command modules under `src/cli/commands`.

Commands are thin in some places and still orchestration-heavy in others.

## Current API layer [#current-api-layer]

The current `src/api` layer contains Storyblok operations for components, stories, assets, datasources, roles, plugins, presets, spaces, inspections, and migrations.

Some of this layer still imports CLI discovery utilities and global configuration. That means it is useful internally, but not yet a perfectly isolated public SDK.

## API v2 [#api-v2]

`api-v2` is the evolving programmatic surface intended for direct GUI and application integration.

Today it includes a client factory, stories, discovery, sync types, and resource wrappers, but it does not yet have full parity with every CLI workflow.

## Documentation stance [#documentation-stance]

The public docs should document the CLI as the stable user surface first. API v2 docs should be explicit about maturity and parity.

---

# Publication Model

Source: /docs/explanation/publication-model

> The Storyblok publication model that drives sb-mig 6.x migration behavior.
Storyblok stories can represent draft and published content at the same time.

## Story states [#story-states]

| State               | Meaning                                                        |
| ------------------- | -------------------------------------------------------------- |
| `draft-only`        | The story is not published.                                    |
| `clean-published`   | The story is published and draft/current matches production.   |
| `dirty-published`   | The story is published and has unpublished draft changes.      |
| `published-unknown` | The story is published but publication metadata is incomplete. |

## Why this matters [#why-this-matters]

Dirty published stories are the dangerous case. Editors have live production content and newer saved draft edits. A migration must not accidentally publish those saved draft edits.

## sb-mig modes [#sb-mig-modes]

`preserve-layers` migrates draft/current and published content separately for dirty published stories.

`collapse-draft` intentionally publishes latest draft/current content for stories that were already published.

`save-only` never publishes.

---

# Artifacts and Audit Trail

Source: /docs/guides/artifacts-audit-trail

> Understand the files sb-mig writes during dry runs and real migrations.
sb-mig writes local files so migration work can be reviewed after the command
exits. Most migration artifacts are written under `sbmig/migrations` by default.

## Stable names [#stable-names]

Use `--fileName` when you want predictable artifact names:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --dry-run \
  --fileName release-2026-06-content
```

With `--fileName release-2026-06-content`, files use that sanitized base name
and do not get a datestamp suffix. Without `--fileName`, sb-mig derives the base
name from `--from` and appends a datestamp to migration artifact files.

## Standard dry-run files [#standard-dry-run-files]

Dry-run content and preset migrations can write:

```txt
dry-run--<name>---story-input-full.json
dry-run--<name>---story-after-full.json
dry-run--<name>---story-to-migrate.json
dry-run--<name>---story-migration-pipeline-summary.json

dry-run--<name>---preset-input-full.json
dry-run--<name>---preset-after-full.json
dry-run--<name>---preset-to-migrate.json
dry-run--<name>---preset-migration-pipeline-summary.json
```

For stories in a publishing mode, dry runs also write:

```txt
dry-run--<name>---language-publish-state-map.json
dry-run--<name>---publication-plan-summary.json
```

## Preserve-layer dry-run files [#preserve-layer-dry-run-files]

When `--publicationMode preserve-layers` is active, sb-mig also previews both
draft/current and latest published payloads:

```txt
dry-run--<name>---draft-current-input-full.json
dry-run--<name>---draft-current-after-full.json
dry-run--<name>---published-layer-input-full.json
dry-run--<name>---published-layer-after-full.json
dry-run--<name>---published-layer-summary.json
```

These files are the proof for dirty published stories: they show the migrated
published layer that would be published, and the migrated draft/current layer
that would be restored afterward.

## Real-run files [#real-run-files]

Real runs write the changed payload and pipeline summary without the `dry-run--`
prefix:

```txt
<name>---story-to-migrate.json
<name>---story-migration-pipeline-summary.json
<name>---language-publish-state-map.json
<name>---story-migration-run-log.jsonl
```

Preset runs write equivalent preset artifacts:

```txt
<name>---preset-to-migrate.json
<name>---preset-migration-pipeline-summary.json
<name>---preset-migration-run-log.jsonl
```

`preserve-layers` real runs also write the draft/current and published-layer
files without the `dry-run--` prefix.

## Run logs [#run-logs]

Real runs write JSONL records after Storyblok write attempts complete. Event
types include:

| Event                     | Meaning                                                      |
| ------------------------- | ------------------------------------------------------------ |
| `update_success`          | Story or preset update succeeded.                            |
| `update_failed`           | Story or preset update failed.                               |
| `publish_success`         | Story language publish succeeded.                            |
| `publish_failed`          | Story language publish failed.                               |
| `publish_skipped`         | Publish was skipped because source state should not publish. |
| `migration_write_summary` | Final per-run write summary.                                 |

Dry runs return before Storyblok writes, so they do not create JSONL run logs.

## Pre-migration backups [#pre-migration-backups]

For real runs with `--migrate-from space`, sb-mig writes source backups before
writing migrated content:

```txt
sbmig/backup/story/before__<name>*.sb.stories.json
sbmig/backup/preset/before__<name>*.sb.presets.json
```

`revert content` also creates a target-space backup before restoring:

```txt
<target-space>--backup-before-revert.sb.stories
```

## Inspection outputs [#inspection-outputs]

`language-publish-state` writes a reusable publish-state JSON file:

```txt
sbmig/language-publish-state/<name>.json
```

`published-layer-export` writes:

```txt
<name>---draft-current-full.json
<name>---published-layer-full.json
<name>---dual-layer-summary.json
```

`story-versions --outputPath` writes exactly to the path you pass.

`inspect component-usage --outputPath` writes exactly to the path you pass:

```txt
sbmig/usage/flex-group-width-child.json
```

The report contains query metadata, filters, totals, and one row per component
usage match. Without `--outputPath`, `inspect component-usage` only prints a
console summary.

---

# Language Publish State

Source: /docs/guides/language-publish-state

> Inspect and preserve translated Storyblok publication state during migrations.
Storyblok translations can be published independently. sb-mig tracks this with a language publish-state map.

## Generate a map [#generate-a-map]

```bash
sb-mig language-publish-state \
  --from 12345 \
  --startsWith about/ \
  --languages all \
  --fileName about-prod
```

The command is read-only against Storyblok and writes a JSON map locally.

## Use a map in migration [#use-a-map-in-migration]

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --publicationMode preserve-layers \
  --publicationLanguages all \
  --languagePublishStatePath sbmig/language-publish-state/about-prod.json \
  --yes
```

When `--languagePublishStatePath` is omitted, sb-mig builds the map automatically for the selected stories.

## Language options [#language-options]

```bash
--publicationLanguages default
--publicationLanguages all
--publicationLanguages default,fr,de
```

`all` resolves to `[default]` plus configured Storyblok language codes from the target space.

## State-sensitive publishing [#state-sensitive-publishing]

sb-mig publishes a language after migration only when the language was published before migration. In `collapse-draft`, dirty published languages are intentionally published from latest draft/current content. In `preserve-layers`, dirty published stories publish the migrated published layer, then restore migrated draft/current content.

---

# Preserve Published Layers

Source: /docs/guides/published-layers

> How sb-mig migrates dirty published Storyblok stories without publishing editor drafts.
Storyblok stories can have two meaningful content layers:

* draft/current content returned by the Management API
* published production content from Story Versions API

When a story is published and has unpublished changes, those layers are intentionally different.

## The problem [#the-problem]

A single-layer migration has to choose a bad tradeoff:

* publish the latest draft and expose editor work early
* save the migrated draft and leave production JSON unmigrated

`--publicationMode preserve-layers` avoids that tradeoff for in-place space migrations.

## Command [#command]

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --publicationMode preserve-layers \
  --publicationLanguages all \
  --dry-run
```

`preserve-layers` currently requires:

* `--migrate-from space`
* the same Storyblok space for `--from` and `--to`

## Dirty published write order [#dirty-published-write-order]

For stories where `published === true` and `unpublished_changes === true`, sb-mig:

1. reads draft/current content from Management API
2. reads the latest `status === "published"` version content
3. migrates both layers independently in memory
4. writes migrated published-layer content
5. publishes that migrated published layer
6. restores migrated draft/current content with `publish:false`

The final state keeps the production and draft split:

```txt
published JSON: migrated production content
draft JSON:     migrated editor draft content
story state:    published=true and unpublished_changes=true
```

## Safety checks [#safety-checks]

Before the dual-layer write, sb-mig re-fetches the story and compares `current_version_id` or `updated_at`. If the story changed after migration input was read, it refuses to overwrite new editor work.

---

# Safe Content Migration

Source: /docs/guides/safe-content-migration

> A production-minded workflow for previewing, auditing, and running Storyblok content migrations.
This is the default workflow for sensitive Storyblok content changes.

## 1. Scope the run [#1-scope-the-run]

Prefer a narrow selector until you trust the migration:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration normalize-design-values \
  --withSlug blog/home \
  --dry-run \
  --fileName blog-home-check
```

Use `--startsWith` for a folder or section:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration normalize-design-values \
  --startsWith blog/ \
  --dry-run \
  --fileName blog-check
```

## 2. Pick a publication mode [#2-pick-a-publication-mode]

Use `preserve-layers` when migrating a real production space in place:

```bash
--publicationMode preserve-layers
```

Use `collapse-draft` for duplicated spaces without reliable Story Versions API history:

```bash
--publicationMode collapse-draft
```

Use `save-only` when nothing should be published:

```bash
--publicationMode save-only
```

## 3. Dry run [#3-dry-run]

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration normalize-design-values \
  --publicationMode preserve-layers \
  --publicationLanguages all \
  --dry-run \
  --fileName production-preview
```

Review the generated files in `sbmig/migrations`.

## 4. Run for real [#4-run-for-real]

Run the same command without `--dry-run`, adding `--yes` only when the reviewed command is final:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration normalize-design-values \
  --publicationMode preserve-layers \
  --publicationLanguages all \
  --fileName production-run \
  --yes
```

## 5. Keep the evidence [#5-keep-the-evidence]

Store migration summaries, changed payloads, and JSONL run logs with the release or change ticket. Those artifacts explain exactly what sb-mig selected, transformed, published, skipped, or failed.

---

# Quickstart

Source: /docs/quickstart

> Install sb-mig, configure Storyblok credentials, and run the first safe commands.
## Requirements [#requirements]

sb-mig 6.x requires Node.js 22 or Node.js 24+.

```bash
node --version
```

## Install [#install]

Install the CLI globally:

```bash
npm install --global sb-mig
```

Or run it through your package manager in project scripts.

## Configure credentials [#configure-credentials]

Create a `.env` file in the project where you run sb-mig:

```bash
STORYBLOK_OAUTH_TOKEN=your-management-token
STORYBLOK_SPACE_ID=12345
STORYBLOK_ACCESS_TOKEN=your-delivery-token
```

The OAuth token is used for Storyblok Management API writes. The access token is used for Delivery API reads, including translated language publish-state checks.

## Optional project config [#optional-project-config]

Create `storyblok.config.js` or `storyblok.config.mjs` when defaults are not enough:

```js
export default {
  componentsDirectories: ["src", "storyblok"],
  schemaFileExt: "sb.js",
  datasourceExt: "sb.datasource.js",
  rolesExt: "sb.roles.js",
  sbmigWorkingDirectory: "sbmig",
};
```

## First read-only checks [#first-read-only-checks]

Start with discovery and inspection commands:

```bash
sb-mig discover components --all
sb-mig language-publish-state --from 12345 --languages all --fileName baseline
sb-mig published-layer-export --from 12345 --startsWith blog/ --fileName blog-layers
```

When you have a component usage query file, run the beta inspection command
without writing to Storyblok:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --all \
  --query flex-group-width-child
```

## First safe write preview [#first-safe-write-preview]

Run migrations with `--dry-run` before a real write:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --publicationMode preserve-layers \
  --dry-run \
  --fileName first-preview
```

Dry runs write local artifacts under `sbmig/migrations` and do not update Storyblok.

---

# Backup, Revert, Remove

Source: /docs/reference/backup-revert-remove

> Backup, restore, and destructive removal commands.
This page covers commands that read local or remote resources for backup,
restore story backups, or delete remote resources.

## backup [#backup]

`backup` reads Storyblok resources and writes local JSON files.

### Command forms [#command-forms]

```bash
sb-mig backup components --all
sb-mig backup components accordion

sb-mig backup component-groups --all
sb-mig backup component-groups hero-group

sb-mig backup roles --all
sb-mig backup roles admin

sb-mig backup datasources --all
sb-mig backup datasources countries

sb-mig backup presets --all
sb-mig backup presets 123456

sb-mig backup component-presets --all
sb-mig backup component-presets --all --metadata
sb-mig backup component-presets accordion

sb-mig backup plugins --all
sb-mig backup plugins my-awesome-plugin

sb-mig backup stories --all
```

### Options [#options]

| Flag or argument    | Applies to                               | Effect                                                                    |
| ------------------- | ---------------------------------------- | ------------------------------------------------------------------------- |
| `--all`             | every backup subcommand                  | Back up all resources of that type.                                       |
| resource name or ID | every backup subcommand except `stories` | Back up one named resource or preset ID.                                  |
| `--metadata`        | `component-presets --all`                | Include selected `package.json` metadata in each component preset backup. |

### Resource output [#resource-output]

| Subcommand          | Output folder or naming behavior                                           |
| ------------------- | -------------------------------------------------------------------------- |
| `components`        | `components` folder, with `all-components` or `component-<name>` prefix.   |
| `component-groups`  | `component-groups` folder.                                                 |
| `roles`             | `roles` folder.                                                            |
| `datasources`       | `datasources` folder.                                                      |
| `presets`           | `presets` folder.                                                          |
| `component-presets` | Configured component preset backup directory, default `component-presets`. |
| `plugins`           | `plugins` folder.                                                          |
| `stories`           | Configured stories backup flow, default base `all-stories-backup`.         |

## revert content [#revert-content]

```bash
sb-mig revert content \
  --from prod-stories-backup \
  --to 12345

sb-mig revert content \
  --from prod-stories-backup \
  --to 12345 \
  --yes
```

`revert content` restores stories from a local story backup file into a target
space.

### Options [#options-1]

| Flag     | Type    | Effect                                                                            |
| -------- | ------- | --------------------------------------------------------------------------------- |
| `--from` | string  | Local story backup file name to discover and load. This is not a source space ID. |
| `--to`   | string  | Target Storyblok space ID.                                                        |
| `--yes`  | boolean | Skip confirmation prompt.                                                         |

### Side effects [#side-effects]

Before restoring, sb-mig writes a target-space story backup named like:

```txt
<target-space>--backup-before-revert.sb.stories
```

Restored stories are updated with `publish:false`; the command does not publish
restored content and does not run migration configs.

## remove [#remove]

`remove` deletes remote resources. It does not write local backups unless you
run a separate backup first.

### components [#components]

```bash
sb-mig remove components --all
sb-mig remove components accordion accordion-item
```

| Form                               | Effect                                                     |
| ---------------------------------- | ---------------------------------------------------------- |
| `remove components --all`          | Remove all components from the configured Storyblok space. |
| `remove components <component...>` | Remove only the provided component names.                  |

### story [#story]

```bash
sb-mig remove story --all --from 12345
```

Deletes all stories from the provided space. This path does not prompt for
confirmation. Back up the space first if the data matters.

### roles and datasources [#roles-and-datasources]

```bash
sb-mig remove roles
sb-mig remove datasources
```

These subcommands are currently no-ops and only log a warning.

---

# CLI Reference

Source: /docs/reference/cli

> Global conventions, command families, and where each command is documented.
```bash
sb-mig [command]
```

`sb-mig` is a Storyblok Management API CLI. Most commands read configuration
from the current project, then either inspect Storyblok state, write local JSON
artifacts, or write back to Storyblok.

## Global commands [#global-commands]

| Form                      | Behavior                                      |
| ------------------------- | --------------------------------------------- |
| `sb-mig`                  | Show top-level help.                          |
| `sb-mig --help`           | Show top-level help.                          |
| `sb-mig help`             | Show top-level help.                          |
| `sb-mig <command> --help` | Show help for one command family.             |
| `sb-mig --version`        | Print the installed `sb-mig` package version. |

## Command families [#command-families]

| Command                  | Purpose                                                                        | Full reference                                                 |
| ------------------------ | ------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| `sync`                   | Synchronize components, roles, datasources, plugins, stories, and assets.      | [sync](/docs/reference/sync)                                   |
| `migrate`                | Run story or preset data migrations.                                           | [migrate](/docs/reference/migrate)                             |
| `inspect`                | Inspect Storyblok content with local query files. `Beta: Needle in a haystack` | [inspection commands](/docs/reference/inspection)              |
| `language-publish-state` | Build a read-only language publish-state map.                                  | [inspection commands](/docs/reference/inspection)              |
| `published-layer-export` | Export draft/current and latest published story layers.                        | [inspection commands](/docs/reference/inspection)              |
| `story-versions`         | Inspect Management API story version history for one story.                    | [inspection commands](/docs/reference/inspection)              |
| `backup`                 | Back up Storyblok resources to local JSON files.                               | [backup, revert, remove](/docs/reference/backup-revert-remove) |
| `revert`                 | Restore stories from a local story backup file.                                | [backup, revert, remove](/docs/reference/backup-revert-remove) |
| `remove`                 | Remove components or all stories from a space.                                 | [backup, revert, remove](/docs/reference/backup-revert-remove) |
| `copy`                   | Copy stories, folders, assets, and asset folders between spaces.               | [utility commands](/docs/reference/utilities)                  |
| `discover`               | Discover local component schemas and migration configs.                        | [utility commands](/docs/reference/utilities)                  |
| `migrations`             | Recognize Backpack package upgrade migrations.                                 | [utility commands](/docs/reference/utilities)                  |
| `init`                   | Initialize Storyblok environment settings.                                     | [utility commands](/docs/reference/utilities)                  |
| `debug`                  | Print resolved config and dependency metadata.                                 | [utility commands](/docs/reference/utilities)                  |

## Command matrix [#command-matrix]

Use [Command Matrix](/docs/reference/command-matrix) as the coverage checklist.
It lists every public command form, valid selectors, options, and write behavior.

## Flag conventions [#flag-conventions]

The public CLI uses long flags. Examples:

```bash
--dry-run
--migrate-from file
--publicationMode preserve-layers
--publicationLanguages default,fr,de
```

`meow`, the CLI parser, exposes kebab-case flags as camelCase internally. The
docs show the form users should type in the shell.

Repeatable flags can be passed more than once:

```bash
--migration migration-a --migration migration-b
--withSlug blog/home --withSlug docs/getting-started
--storyId 111 --storyId 222
```

## Config fallback [#config-fallback]

Most Storyblok commands read `storyblok.config.js` or `storyblok.config.mjs`
from the current working directory and merge it with `.env` defaults.

Important environment values:

| Variable                                   | Used for                                                         |
| ------------------------------------------ | ---------------------------------------------------------------- |
| `STORYBLOK_SPACE_ID`                       | Default space ID when a command falls back to configured space.  |
| `STORYBLOK_OAUTH_TOKEN`                    | Management API access.                                           |
| `STORYBLOK_ACCESS_TOKEN`                   | Delivery API token for translated language publish-state checks. |
| `NEXT_PUBLIC_STORYBLOK_ACCESS_TOKEN`       | Delivery API fallback token.                                     |
| `NEXT_PUBLIC_STORYBLOK_MANAGEMENT_API_URL` | Management API URL override.                                     |
| `NEXT_PUBLIC_STORYBLOK_DELIVERY_API_URL`   | Delivery API URL override.                                       |
| `NEXT_PUBLIC_STORYBLOK_GRAPHQL_API_URL`    | GraphQL API URL override.                                        |

Some commands say `--from` or `--to` is required. In implementation, several
older commands fall back to configured `spaceId` when the flag is omitted. For
production usage, pass explicit `--from` and `--to` so command logs and
artifacts are unambiguous.

## Confirmation flags [#confirmation-flags]

`--yes` skips prompts for commands that call the confirmation helper. It is
used by:

* `sync components --all --ssot`
* destructive `sync content` directions that delete target stories before sync
* `migrate content`
* `migrate presets`
* `migrate continue`
* `revert content`

`--dry-run` skips writes only for commands that explicitly support it. It is
not a universal global flag.

## Beta feature labels [#beta-feature-labels]

Some commands are documented with a beta label when they ship as focused
investigation tools before becoming broader workflows.

| Label                        | Meaning                                                                                                |
| ---------------------------- | ------------------------------------------------------------------------------------------------------ |
| `Beta: Needle in a haystack` | A read-only beta inspection feature for finding rare Storyblok component patterns across many stories. |

---

# Command Matrix

Source: /docs/reference/command-matrix

> Coverage checklist for every public sb-mig command form and option family.
This page is the coverage checklist for the public CLI surface in
`sb-mig@6.2.0-beta.1`.

## Top level [#top-level]

| Command form              | Reads Storyblok | Writes files | Writes Storyblok |
| ------------------------- | --------------- | ------------ | ---------------- |
| `sb-mig`                  | No              | No           | No               |
| `sb-mig --help`           | No              | No           | No               |
| `sb-mig help`             | No              | No           | No               |
| `sb-mig <command> --help` | No              | No           | No               |
| `sb-mig --version`        | No              | No           | No               |

## sync [#sync]

| Command form                              | Required selector | Optional flags                                            | Writes Storyblok                    |
| ----------------------------------------- | ----------------- | --------------------------------------------------------- | ----------------------------------- |
| `sb-mig sync components --all`            | `--all`           | `--presets`, `--dry-run`                                  | Yes unless `--dry-run`              |
| `sb-mig sync components --all --ssot`     | `--all --ssot`    | `--presets`, `--dry-run`, `--yes`                         | Yes unless `--dry-run`; destructive |
| `sb-mig sync components <component...>`   | component names   | `--presets`, `--packageName`, `--dry-run`                 | Yes unless `--dry-run`              |
| `sb-mig sync roles --all`                 | `--all`           | `--dry-run`                                               | Yes unless `--dry-run`              |
| `sb-mig sync roles <role...>`             | role names        | `--dry-run`                                               | Yes unless `--dry-run`              |
| `sb-mig sync datasources --all`           | `--all`           | `--dry-run`                                               | Yes unless `--dry-run`              |
| `sb-mig sync datasources <datasource...>` | datasource names  | `--dry-run`                                               | Yes unless `--dry-run`              |
| `sb-mig sync plugins <plugin...>`         | plugin names      | `--dry-run`                                               | Yes unless `--dry-run`              |
| `sb-mig sync content --all`               | `--all`           | `--from`, `--to`, `--syncDirection`, `--dry-run`, `--yes` | Depends on direction                |
| `sb-mig sync content --stories`           | `--stories`       | `--from`, `--to`, `--syncDirection`, `--dry-run`, `--yes` | Depends on direction                |
| `sb-mig sync content --assets`            | `--assets`        | `--from`, `--to`, `--syncDirection`, `--dry-run`          | Depends on direction                |

Content sync directions:

| Direction          | Valid with                                                       | Destructive behavior                                                 |
| ------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------------- |
| `fromSpaceToFile`  | `--all`, `--stories`, `--assets`                                 | No target deletion; writes local backup files.                       |
| `fromFileToSpace`  | `--all`, `--stories` for stories; assets not implemented         | Non-dry story sync backs up and deletes target stories before write. |
| `fromSpaceToSpace` | `--all`, `--stories`, `--assets`                                 | Non-dry story sync backs up and deletes target stories before write. |
| `fromAWSToSpace`   | `--stories`; `--all` calls story path and unsupported asset path | Imports Content Hub/AWS story data into target space.                |

## migrate [#migrate]

| Command form                   | Required selector                          | Optional flags                                                                                                                                                                                                                                            | Writes Storyblok                          |
| ------------------------------ | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `sb-mig migrate content --all` | `--all`, `--migration`                     | `--from`, `--to`, `--withSlug`, `--startsWith`, `--migrate-from`, `--fromFilePath`, `--publicationMode`, `--publicationLanguages`, `--languagePublishStatePath`, `--migrationComponentAlias`, `--migrationComponents`, `--fileName`, `--dry-run`, `--yes` | Yes unless `--dry-run`                    |
| `sb-mig migrate presets --all` | `--all`, exactly one `--migration`         | `--from`, `--to`, `--migrate-from`, `--fromFilePath`, `--migrationComponentAlias`, `--migrationComponents`, `--fileName`, `--dry-run`, `--yes`                                                                                                            | Yes unless `--dry-run`                    |
| `sb-mig migrate continue`      | none (auto-discovers the dry-run manifest) | `--manifest`, `--yes`                                                                                                                                                                                                                                     | Yes (replays a prior `content --dry-run`) |

Content migration constraints:

| Combination                                                             | Status                                                                                                                           |
| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `--publicationMode preserve-layers --from A --to A`                     | Valid. Default and safest in-place production mode.                                                                              |
| `--publicationMode preserve-layers --from A --to B`                     | Rejected. Preserve-layers currently requires same source and target space.                                                       |
| `--publicationMode preserve-layers --migrate-from file`                 | Rejected. Preserve-layers needs Story Versions API access from a source space.                                                   |
| `--publicationMode collapse-draft --publicationLanguages default,fr,de` | Valid. Publishes selected languages for stories/languages that were publishable in source state.                                 |
| `--publicationMode save-only`                                           | Valid. Saves only and never publishes.                                                                                           |
| `--publicationMode save-only --publicationLanguages ...`                | Rejected. Save-only does not publish languages.                                                                                  |
| `--publicationMode save-only --languagePublishStatePath ...`            | Rejected. Save-only does not use publish-state maps.                                                                             |
| `--publish`, `--publishLanguages`, `--preservePublishedLayer`           | Rejected legacy flags.                                                                                                           |
| `migrate content <component-name...>`                                   | Help lists this form, but current built `6.1.1-beta.1` rejects it. Use `--all --migrationComponents <migration>:<component...>`. |

Preset migration constraints:

| Combination                   | Status                |
| ----------------------------- | --------------------- |
| one `--migration`             | Valid.                |
| multiple `--migration` values | Rejected for presets. |
| `--publicationMode`           | Rejected for presets. |
| `--publicationLanguages`      | Rejected for presets. |
| `--languagePublishStatePath`  | Rejected for presets. |

## inspection [#inspection]

<span className="inline-flex items-center rounded-md border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-800 dark:text-amber-300">
  Beta: Needle in a haystack
</span>

{" "}

applies to `inspect component-usage`.

| Command form                                                                          | Required selector                   | Optional flags                                                                             | Writes Storyblok |
| ------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------ | ---------------- |
| `sb-mig inspect component-usage --from <space> --all --query <query>`                 | `--all`, `--query`                  | `--outputPath`                                                                             | No               |
| `sb-mig inspect component-usage --from <space> --withSlug <slug> --query <query>`     | one or more `--withSlug`, `--query` | `--outputPath`                                                                             | No               |
| `sb-mig inspect component-usage --from <space> --startsWith <prefix> --query <query>` | `--startsWith`, `--query`           | `--outputPath`                                                                             | No               |
| `sb-mig language-publish-state --from <space>`                                        | `--from`                            | `--accessToken`, `--languages`, `--withSlug`, `--startsWith`, `--fileName`, `--outputPath` | No               |
| `sb-mig story-versions --from <space> --storyId <id>`                                 | `--from`, `--storyId`               | `--showContent`, `--page`, `--perPage`, `--raw`, `--outputPath`                            | No               |
| `sb-mig story-versions --from <space> --withSlug <slug>`                              | `--from`, `--withSlug`              | `--showContent`, `--page`, `--perPage`, `--raw`, `--outputPath`                            | No               |
| `sb-mig published-layer-export --from <space> --all`                                  | `--from`, one selector              | `--fileName`, `--outputPath`, `--versionsPerPage`, `--maxVersionPages`                     | No               |
| `sb-mig published-layer-export --from <space> --storyId <id>`                         | `--from`, one or more `--storyId`   | `--fileName`, `--outputPath`, `--versionsPerPage`, `--maxVersionPages`                     | No               |
| `sb-mig published-layer-export --from <space> --withSlug <slug>`                      | `--from`, one or more `--withSlug`  | `--fileName`, `--outputPath`, `--versionsPerPage`, `--maxVersionPages`                     | No               |
| `sb-mig published-layer-export --from <space> --startsWith <prefix>`                  | `--from`, `--startsWith`            | `--fileName`, `--outputPath`, `--versionsPerPage`, `--maxVersionPages`                     | No               |

Inspection constraints:

| Combination                                                     | Status                                           |
| --------------------------------------------------------------- | ------------------------------------------------ |
| `inspect component-usage --query <query> --all`                 | Valid. Scans all non-folder stories.             |
| `inspect component-usage --query <query> --withSlug <slug>`     | Valid. `--withSlug` is repeatable.               |
| `inspect component-usage --query <query> --startsWith <prefix>` | Valid. Scans non-folder stories under a prefix.  |
| `inspect component-usage --all --withSlug <slug>`               | Rejected. Pass exactly one story selection mode. |
| `inspect component-usage --all --startsWith <prefix>`           | Rejected. Pass exactly one story selection mode. |
| `inspect component-usage` without `--query`                     | Rejected. Query file name or path is required.   |
| `inspect component-usage --dry-run`                             | Not supported. The command is already read-only. |

## backup, revert, remove [#backup-revert-remove]

| Command form                                       | Required selector | Optional flags | Writes Storyblok               |
| -------------------------------------------------- | ----------------- | -------------- | ------------------------------ |
| `sb-mig backup components --all`                   | `--all`           | none           | No                             |
| `sb-mig backup components <component>`             | component name    | none           | No                             |
| `sb-mig backup component-groups --all`             | `--all`           | none           | No                             |
| `sb-mig backup component-groups <group>`           | group name        | none           | No                             |
| `sb-mig backup roles --all`                        | `--all`           | none           | No                             |
| `sb-mig backup roles <role>`                       | role name         | none           | No                             |
| `sb-mig backup datasources --all`                  | `--all`           | none           | No                             |
| `sb-mig backup datasources <datasource>`           | datasource name   | none           | No                             |
| `sb-mig backup presets --all`                      | `--all`           | none           | No                             |
| `sb-mig backup presets <preset-id>`                | preset ID         | none           | No                             |
| `sb-mig backup component-presets --all`            | `--all`           | `--metadata`   | No                             |
| `sb-mig backup component-presets <component>`      | component name    | none           | No                             |
| `sb-mig backup plugins --all`                      | `--all`           | none           | No                             |
| `sb-mig backup plugins <plugin>`                   | plugin name       | none           | No                             |
| `sb-mig backup stories --all`                      | `--all`           | none           | No                             |
| `sb-mig revert content --from <file> --to <space>` | `--from`, `--to`  | `--yes`        | Yes                            |
| `sb-mig remove components --all`                   | `--all`           | none           | Yes                            |
| `sb-mig remove components <component...>`          | component names   | none           | Yes                            |
| `sb-mig remove story --all --from <space>`         | `--all`, `--from` | none           | Yes, destructive and no prompt |
| `sb-mig remove roles`                              | none              | none           | No-op warning                  |
| `sb-mig remove datasources`                        | none              | none           | No-op warning                  |

## utilities [#utilities]

| Command form                                                                | Required selector                       | Optional flags                                                           | Writes Storyblok       |
| --------------------------------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------ | ---------------------- |
| `sb-mig copy stories --source <story> --destination <folder>`               | `--source`                              | `--from`, `--to`, `--destination`, `--mode`, `--dry-run`, `--outputPath` | Yes unless `--dry-run` |
| `sb-mig copy stories --source <folder> --destination <folder>`              | `--source`                              | `--from`, `--to`, `--destination`, `--mode`, `--dry-run`, `--outputPath` | Yes unless `--dry-run` |
| `sb-mig copy stories --source <folder>/* --destination <folder>`            | `--source`                              | `--from`, `--to`, `--destination`, `--dry-run`, `--outputPath`           | Yes unless `--dry-run` |
| `sb-mig copy assets --all`                                                  | `--all`                                 | `--from`, `--to`, `--dry-run`, `--outputPath`                            | Yes unless `--dry-run` |
| `sb-mig copy assets --asset <id-or-file>`                                   | `--asset`                               | `--from`, `--to`, `--dry-run`, `--outputPath`                            | Yes unless `--dry-run` |
| `sb-mig copy assets --assetFolder <id-or-path>`                             | `--assetFolder`                         | `--from`, `--to`, `--dry-run`, `--outputPath`                            | Yes unless `--dry-run` |
| `sb-mig copy assets --referenced-by-stories --source <story-or-folder>`     | `--referenced-by-stories`, `--source`   | `--from`, `--to`, `--mode`, `--dry-run`, `--outputPath`                  | Yes unless `--dry-run` |
| `sb-mig discover components --all`                                          | `--all`                                 | `--write`, `--file`                                                      | No                     |
| `sb-mig discover migrations --all`                                          | `--all`                                 | none                                                                     | No                     |
| `sb-mig migrations recognize --from <version>`                              | `--from`                                | `--to`                                                                   | No                     |
| `sb-mig init project --spaceId <id> --oauthToken <token> --region <region>` | `--spaceId`, `--oauthToken`, `--region` | `--gtmToken`                                                             | Yes                    |
| `sb-mig debug`                                                              | none                                    | none                                                                     | No                     |

---

# Inspection Commands

Source: /docs/reference/inspection

> Read-only commands for understanding Storyblok state before migration.
These commands are read-only against Storyblok. They exist to make migration
state inspectable before you run write commands.

## inspect component-usage [#inspect-component-usage]

<span className="inline-flex items-center rounded-md border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-800 dark:text-amber-300">
  Beta: Needle in a haystack
</span>

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --all \
  --query flex-group-width-child
```

`inspect component-usage` scans selected Storyblok stories with a local query
file. Use it when you need to find rare component patterns, such as a specific
component with a child component that has a particular field.

The command is read-only against Storyblok. It does not support `--dry-run`
because it never writes to Storyblok. It writes a local JSON report only when
`--outputPath` is passed.

### Query files [#query-files]

Query files are normal JavaScript modules. The first implementation supports:

```txt
*.sb.query.js
*.sb.query.cjs
*.sb.query.mjs
```

TypeScript query files are planned, but not supported by this command yet.

The query file must default-export an object with a `name` and a `match`
function:

```js
export default {
  name: "flex-group-width-child",

  match(node, context) {
    if (node.component !== "flex-group") {
      return null;
    }

    return {
      story: context.story.full_slug,
      path: context.path,
    };
  },
};
```

`match(node, context)` is called for every Storyblok component node found under
`story.content`.

| Return value          | Meaning                                       |
| --------------------- | --------------------------------------------- |
| `null` or `undefined` | No match.                                     |
| `true`                | Match with no extra details.                  |
| object                | Match with details stored in the JSON report. |

The context object contains:

| Field               | Meaning                                                       |
| ------------------- | ------------------------------------------------------------- |
| `context.story`     | Story metadata such as `id`, `name`, `slug`, and `full_slug`. |
| `context.path`      | Component path, for example `content.body[0]`.                |
| `context.parent`    | Parent component object when there is one.                    |
| `context.ancestors` | Ancestor component objects.                                   |

When `--query` is a name, sb-mig searches configured local component
directories, such as `src` and `storyblok`, for a matching query file:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --all \
  --query flex-group-width-child
```

When `--query` is a path, sb-mig loads that file directly:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --startsWith landing-pages \
  --query ./queries/flex-group-width-child.sb.query.js
```

### Example: flex group with width child [#example-flex-group-with-width-child]

Create a file such as:

```txt
src/storyblok-queries/flex-group-width-child.sb.query.js
```

```js
const hasValue = (value) =>
  value !== undefined && value !== null && value !== "";

const directArrayChildren = (node) =>
  Object.values(node)
    .filter(Array.isArray)
    .flat()
    .filter((item) => item && typeof item === "object" && item.component);

export default {
  name: "flex-group-width-child",
  description:
    "Find flex groups with vertical or reverse direction and children with width.",

  match(node) {
    if (node.component !== "flex-group") {
      return null;
    }

    if (!["vertical", "reverse"].includes(node.direction)) {
      return null;
    }

    const childrenWithWidth = directArrayChildren(node).filter((child) =>
      hasValue(child.width),
    );

    if (childrenWithWidth.length === 0) {
      return null;
    }

    return {
      childMatches: childrenWithWidth.length,
      matchingChildUids: childrenWithWidth.map((child) => child._uid),
    };
  },
};
```

Run it by query name:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --all \
  --query flex-group-width-child
```

Or run it by path and write the full report:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --startsWith landing-pages \
  --query ./src/storyblok-queries/flex-group-width-child.sb.query.js \
  --outputPath sbmig/usage/flex-group-width-child.json
```

### Selectors [#selectors]

Pass exactly one selector:

| Selector       | Type              | Effect                                                    |
| -------------- | ----------------- | --------------------------------------------------------- |
| `--all`        | boolean           | Inspect all non-folder stories.                           |
| `--withSlug`   | repeatable string | Inspect exact story `full_slug` values.                   |
| `--startsWith` | string            | Inspect stories whose `full_slug` starts with the prefix. |

### Options [#options]

| Flag           | Type   | Default              | Effect                                                                 |
| -------------- | ------ | -------------------- | ---------------------------------------------------------------------- |
| `--from`       | string | configured `spaceId` | Source space ID to inspect. Pass it explicitly for repeatable reports. |
| `--query`      | string | none                 | Query file name or direct path. Required.                              |
| `--outputPath` | string | none                 | Write the JSON report to this path.                                    |

### Output [#output]

The command prints a summary:

```txt
Component usage inspection
Space: 12345
Query: flex-group-width-child
Stories scanned: 1240
Stories matched: 37
Total matches: 52
Matches by story:
- home: 2
- campaign/summer: 1
```

When `--outputPath` is passed, the JSON report includes:

```txt
queryName
spaceId
generatedAt
filters
totals.storiesScanned
totals.storiesMatched
totals.matches
matches[]
```

Each match includes story identifiers, component name, `_uid`, path, optional
parent component metadata, and any details returned from the query.

### Invalid combinations [#invalid-combinations]

| Combination                                       | Result                                           |
| ------------------------------------------------- | ------------------------------------------------ |
| Missing `--query`                                 | Rejected.                                        |
| Missing `--all`, `--withSlug`, and `--startsWith` | Rejected.                                        |
| More than one selector family                     | Rejected.                                        |
| `--dry-run`                                       | Not supported. The command is already read-only. |

Use this command for inspection and audit work. Do not use it to migrate,
publish, update, or delete Storyblok content.

## language-publish-state [#language-publish-state]

```bash
sb-mig language-publish-state --from 12345
```

Reads source stories and writes a JSON map of publication state by story and
language.

### Options [#options-1]

| Flag            | Type              | Default                 | Effect                                                                       |
| --------------- | ----------------- | ----------------------- | ---------------------------------------------------------------------------- |
| `--from`        | string            | none                    | Source space ID. Required.                                                   |
| `--accessToken` | string            | configured access token | Delivery API token override for translated language checks.                  |
| `--languages`   | string            | `all`                   | `all`, `default`, or comma-separated language codes such as `default,fr,de`. |
| `--withSlug`    | repeatable string | none                    | Inspect exact story `full_slug` values.                                      |
| `--startsWith`  | string            | none                    | Inspect stories whose `full_slug` starts with the prefix.                    |
| `--fileName`    | string            | derived name            | Stable output base name under `sbmig/language-publish-state`.                |
| `--outputPath`  | string            | default folder          | Explicit output path for the generated JSON file.                            |

### Examples [#examples]

```bash
sb-mig language-publish-state \
  --from 12345 \
  --startsWith about/ \
  --languages all \
  --fileName about-prod

sb-mig language-publish-state \
  --from 12345 \
  --accessToken xxx \
  --withSlug about/testimonials \
  --languages default,fr
```

Default-language state comes from the Management API. Translated language state
uses Delivery API published and draft comparisons, so a Delivery API token is
required when translated languages are requested and no configured token exists.

## published-layer-export [#published-layer-export]

```bash
sb-mig published-layer-export --from 12345 --startsWith about/
```

Exports selected draft/current stories, latest published Story Versions API
content, and a comparison summary.

### Selectors [#selectors-1]

Pass one selector family for clarity:

| Selector       | Type              | Effect                                    |
| -------------- | ----------------- | ----------------------------------------- |
| `--all`        | boolean           | Export all non-folder stories.            |
| `--storyId`    | repeatable string | Export exact story IDs.                   |
| `--withSlug`   | repeatable string | Export exact story `full_slug` values.    |
| `--startsWith` | string            | Export non-folder stories under a prefix. |

If multiple selector families are passed, implementation priority is
`--storyId`, then `--withSlug`, then `--startsWith`, then `--all`.

### Options [#options-2]

| Flag                | Type   | Default                        | Effect                                            |
| ------------------- | ------ | ------------------------------ | ------------------------------------------------- |
| `--from`            | string | none                           | Source space ID. Required.                        |
| `--fileName`        | string | derived from selector          | Stable output base name.                          |
| `--outputPath`      | string | `sbmig/published-layer-export` | Output directory.                                 |
| `--versionsPerPage` | number | `25`                           | Story versions per page.                          |
| `--maxVersionPages` | number | `4`                            | Maximum story version pages to inspect per story. |

### Output files [#output-files]

```txt
<name>---draft-current-full.json
<name>---published-layer-full.json
<name>---dual-layer-summary.json
```

### Examples [#examples-1]

```bash
sb-mig published-layer-export \
  --from 12345 \
  --withSlug about/contact \
  --fileName contact-layer-check

sb-mig published-layer-export \
  --from 12345 \
  --storyId 178888427520390

sb-mig published-layer-export \
  --from 12345 \
  --startsWith about/ \
  --versionsPerPage 50 \
  --maxVersionPages 8
```

## story-versions [#story-versions]

```bash
sb-mig story-versions --from 12345 --storyId 98765
sb-mig story-versions --from 12345 --withSlug about/contact
```

Reads Management API `story_versions` for one story.

### Selectors [#selectors-2]

| Selector     | Type   | Effect                                                                        |
| ------------ | ------ | ----------------------------------------------------------------------------- |
| `--storyId`  | string | Inspect one story ID. Required unless `--withSlug` is passed.                 |
| `--withSlug` | string | Resolve one `full_slug` to a story ID. Required unless `--storyId` is passed. |

### Options [#options-3]

| Flag            | Type    | Default | Effect                                                           |
| --------------- | ------- | ------- | ---------------------------------------------------------------- |
| `--from`        | string  | none    | Source space ID. Required.                                       |
| `--showContent` | boolean | `true`  | Ask Storyblok to include version content.                        |
| `--page`        | number  | `1`     | Versions page.                                                   |
| `--perPage`     | number  | `25`    | Versions per page.                                               |
| `--raw`         | boolean | `false` | Print the raw Storyblok API response instead of compact summary. |
| `--outputPath`  | string  | none    | Write JSON output to this file instead of stdout.                |

### Examples [#examples-2]

```bash
sb-mig story-versions --from 12345 --storyId 98765

sb-mig story-versions \
  --from 12345 \
  --withSlug tours/europe \
  --raw \
  --outputPath sbmig/story-versions/tours-europe.raw.json
```

When slug resolution fails, the command searches close Storyblok slugs and
prints possible matches.

---

# migrate

Source: /docs/reference/migrate

> Run content and preset migration pipelines.
```bash
sb-mig migrate content --all --from <space> --to <space> --migration <name>
sb-mig migrate presets --all --from <space-or-file> --to <space> --migration <name>
sb-mig migrate continue
```

`migrate` loads local migration config files, applies them in memory, writes
artifacts, and then writes changed stories or presets unless `--dry-run` is
passed. `migrate continue` finishes a previous `--dry-run` by replaying its
saved result to Storyblok without re-running the preparation phase.

## Shared flags [#shared-flags]

Exact flag names on this page include fromFilePath, migrationComponentAlias,
migrationComponents, withSlug, startsWith, publicationMode,
publicationLanguages, languagePublishStatePath, and fileName.

| Flag                        | Type              | Applies to       | Effect                                                                                                        |
| --------------------------- | ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------- |
| `--from`                    | string            | content, presets | Source space ID. With `--migrate-from file`, it can be a local backup file name.                              |
| `--fromFilePath`            | string            | content, presets | Direct path to a stories or presets JSON file when `--migrate-from file` is used.                             |
| `--to`                      | string            | content, presets | Target Storyblok space ID.                                                                                    |
| `--migrate-from`            | `space` or `file` | content, presets | Read source items from Storyblok or local JSON. Default: `space`.                                             |
| `--migration`               | repeatable string | content, presets | Migration config name without extension. Content supports multiple ordered values. Presets support one value. |
| `--migrationComponentAlias` | repeatable string | content, presets | Add mapper aliases. Format: `<migration>:<source>=<alias1>,<alias2>`.                                         |
| `--migrationComponents`     | repeatable string | content, presets | Override exact component scope. Format: `<migration>:<component1>,<component2>`.                              |
| `--dry-run`                 | boolean           | content, presets | Apply migration in memory and write artifacts without Storyblok writes.                                       |
| `--fileName`                | string            | content, presets | Stable artifact base name. Also disables datestamped artifact names.                                          |
| `--yes`                     | boolean           | content, presets | Skip confirmation prompt for real writes.                                                                     |

## content [#content]

### Primary form [#primary-form]

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --dry-run
```

`--all` means "select all non-folder stories, then run the component mappers
defined by the migration config." It does not mean every component must change.
Only stories whose JSON changes are written to the `*-to-migrate` artifact and,
in real runs, to Storyblok.

### Content-only flags [#content-only-flags]

| Flag                         | Type              | Effect                                                                                    |
| ---------------------------- | ----------------- | ----------------------------------------------------------------------------------------- |
| `--all`                      | boolean           | Select all non-folder stories in scope.                                                   |
| `--withSlug`                 | repeatable string | Select exact story `full_slug` values.                                                    |
| `--startsWith`               | string            | Select stories whose `full_slug` starts with the prefix.                                  |
| `--publicationMode`          | enum              | `preserve-layers`, `collapse-draft`, or `save-only`. Default: `preserve-layers`.          |
| `--publicationLanguages`     | string            | `default`, `all`, or comma-separated language codes. Default for publishing modes: `all`. |
| `--languagePublishStatePath` | string            | Reuse a JSON map generated by `language-publish-state`.                                   |

### Story selection [#story-selection]

Select all non-folder stories:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name
```

Select exact slugs:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --withSlug blog/home \
  --withSlug docs/getting-started
```

Select a subtree by prefix:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --startsWith blog/
```

### Scoped component migrations [#scoped-component-migrations]

Use `--migrationComponents` to restrict one migration to specific component
names:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration colorPickerModeValues \
  --migrationComponents colorPickerModeValues:sb-section,sb-tour-page-section
```

Use `--migrationComponentAlias` when another component should reuse an existing
mapper:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration colorPickerModeValues \
  --migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button
```

You can combine aliases and overrides. Alias first, then include the alias in
the override list if you want the migration scoped to only those components:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration colorPickerModeValues \
  --migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button \
  --migrationComponents colorPickerModeValues:sb-button,sb-open-drift-button
```

The help output currently lists `sb-mig migrate content <component-name ...>`.
In `6.1.1-beta.1`, the built CLI rejects that combination because the default
`migrateFrom` flag is present during command matching. Use `--all` plus
`--migrationComponents` for deterministic scoped migrations.

### Multiple migration steps [#multiple-migration-steps]

Content migrations can run multiple configs in order:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-a \
  --migration migration-b \
  --migration migration-c \
  --yes
```

Each step gets its own pipeline summary entry. If a migration has a matching
validator, the validator runs after that step.

### File input [#file-input]

Read from a discovered local backup file name:

```bash
sb-mig migrate content --all \
  --migrate-from file \
  --from file-with-stories \
  --to 12345 \
  --migration migration-name
```

Read from an explicit file path:

```bash
sb-mig migrate content --all \
  --migrate-from file \
  --fromFilePath sbmig/migrations/dry-run--12345---story-to-migrate.json \
  --to 12345 \
  --migration migration-a \
  --migration migration-b
```

When `--fromFilePath` is used and `--from` is omitted, sb-mig derives the
artifact base name from the file path.

## Publication modes [#publication-modes]

| Mode              | Behavior                                                                                                                                                              | Constraints                                                                                       |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `preserve-layers` | Default. Migrates draft/current and latest published layer for dirty published stories, publishes the migrated published layer, then restores migrated draft/current. | Requires `--migrate-from space` and `--from` equal to `--to`.                                     |
| `collapse-draft`  | Migrates draft/current only. Published stories can be republished from the migrated draft/current layer. Dirty draft content becomes the published content.           | Works for duplicated-space workflows where preserving a separate published layer is not required. |
| `save-only`       | Saves migrated draft/current content and never publishes.                                                                                                             | Cannot be combined with `--publicationLanguages` or `--languagePublishStatePath`.                 |

Legacy flags are rejected:

| Old flag                   | Replacement                                                               |
| -------------------------- | ------------------------------------------------------------------------- |
| `--publish`                | `--publicationMode preserve-layers` or `--publicationMode collapse-draft` |
| `--publishLanguages`       | `--publicationLanguages`                                                  |
| `--preservePublishedLayer` | `--publicationMode preserve-layers`                                       |

## Publication languages [#publication-languages]

```bash
--publicationLanguages default
--publicationLanguages all
--publicationLanguages default,fr,de
```

`default` maps to Storyblok's default-language publish token. `all` resolves the
target space languages and publishes `[default]` plus configured language codes.

When a publishing mode is active and `--languagePublishStatePath` is omitted,
sb-mig builds a language publish-state map automatically for the selected
stories. For sensitive runs, generate the map first:

```bash
sb-mig language-publish-state \
  --from 12345 \
  --startsWith blog/ \
  --languages all \
  --fileName blog-prod

sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --publicationMode preserve-layers \
  --publicationLanguages all \
  --languagePublishStatePath sbmig/language-publish-state/blog-prod.json \
  --yes
```

## presets [#presets]

### Valid forms [#valid-forms]

```bash
sb-mig migrate presets --all \
  --from 12345 \
  --to 12345 \
  --migration preset-migration \
  --dry-run

sb-mig migrate presets --all \
  --migrate-from file \
  --fromFilePath sbmig/presets/presets-backup.json \
  --to 12345 \
  --migration preset-migration
```

### Preset constraints [#preset-constraints]

| Constraint                               | Reason                                                                  |
| ---------------------------------------- | ----------------------------------------------------------------------- |
| Exactly one `--migration` value          | Preset migration pipelines do not support multiple ordered configs yet. |
| `--publicationMode` is rejected          | Presets cannot be published.                                            |
| `--publicationLanguages` is rejected     | Presets cannot be published by language.                                |
| `--languagePublishStatePath` is rejected | Presets do not use story publish-state maps.                            |

Before a real preset run, sb-mig backs up all remote presets to
`presets-backup`.

## continue [#continue]

`continue` finishes a previous `migrate content --dry-run` by writing its
already-computed result to Storyblok. It skips the expensive preparation a dry
run already did — pulling stories, applying mappers in memory, and fetching
published layers and language state — and replays only the writes. On large
spaces this turns the "dry run, then real run" workflow from roughly double the
cost into one preparation plus a fast write, so always running a dry run first
no longer doubles the wait.

`continue` assumes a content freeze between the dry run and the continue run.
The source space must not change in between: continue trusts the dry run's
result and does not re-read or diff the space (no drift detection).

### Primary form [#primary-form-1]

```bash
sb-mig migrate continue
```

`continue` discovers the dry run's continue manifest in `sbmig/migrations`,
prints a summary of what it will write (space, publication mode, languages,
story counts, and the artifacts it will use), and prompts for confirmation
before writing.

### continue-only flags [#continue-only-flags]

| Flag         | Type    | Effect                                                                                                            |
| ------------ | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `--manifest` | string  | Continue manifest filename inside `sbmig/migrations`. Only needed when more than one dry-run manifest is present. |
| `--yes`      | boolean | Skip the confirmation prompt for the write.                                                                       |

### Manifest discovery [#manifest-discovery]

| Situation                                           | Behavior                                                        |
| --------------------------------------------------- | --------------------------------------------------------------- |
| Exactly one continue manifest in `sbmig/migrations` | Used automatically.                                             |
| More than one                                       | Stops and lists them; pass `--manifest <filename>` to pick one. |
| None                                                | Errors and points you at `migrate content … --dry-run`.         |

### Equivalent to a real run [#equivalent-to-a-real-run]

`continue` writes exactly what a real `migrate content` would: it updates
`applied-backpack-migrations.json`, performs the same Storyblok writes
(including the preserve-layers dual-layer write for dirty published stories),
and writes a migration run log tagged with the originating manifest.

It performs no bulk story fetch, no in-memory migration, no published-layer
fetch, and no language-state fetch. The only read it still makes is the
existing per-story safety re-fetch inside the preserve-layers dual-layer write.

## Artifacts and side effects [#artifacts-and-side-effects]

For `--migrate-from space` real runs, sb-mig writes a pre-migration backup under
`sbmig/backup/story` or `sbmig/backup/preset`.

All content and preset runs write migration artifacts under `sbmig/migrations`.
Dry runs return before Storyblok writes. Real runs also write a JSONL migration
run log after Storyblok write attempts complete.

A content dry run additionally writes a `*-continue-manifest` file (and, for
preserve-layers, a `*-dirty-published-records` file) that `migrate continue`
uses to replay the run. The dry run's `*-input-full` artifacts are the
pre-migration snapshot for that workflow, since `continue` does not re-read the
space to take its own backup.

---

# sync

Source: /docs/reference/sync

> Sync schemas, roles, datasources, plugins, stories, and assets.
```bash
sb-mig sync <resource> [selector] [options]
```

`sync` pushes local schema-like resources to Storyblok or moves content between
spaces/files. The supported resources are `components`, `roles`, `datasources`,
`plugins`, and `content`.

## components [#components]

### Valid forms [#valid-forms]

```bash
sb-mig sync components --all
sb-mig sync components --all --dry-run
sb-mig sync components --all --presets
sb-mig sync components --all --presets --dry-run
sb-mig sync components --all --ssot --yes
sb-mig sync components --all --ssot --presets --yes
sb-mig sync components --all --ssot --dry-run
sb-mig sync components accordion accordion-item
sb-mig sync components accordion accordion-item --presets
sb-mig sync components accordion --packageName @scope/package
sb-mig sync components accordion --dry-run
```

### Options [#options]

| Flag            | Type       | Effect                                                                                                            |
| --------------- | ---------- | ----------------------------------------------------------------------------------------------------------------- |
| `--all`         | boolean    | Sync every discovered local component schema.                                                                     |
| component names | positional | Sync only the named local component schemas.                                                                      |
| `--presets`     | boolean    | Also sync component presets and set default presets.                                                              |
| `--ssot`        | boolean    | Single Source of Truth mode for `--all`; removes GUI-only remote components and replaces them with code versions. |
| `--packageName` | string     | Resolve provided components from an external package.                                                             |
| `--dry-run`     | boolean    | Preview component changes without Storyblok writes.                                                               |
| `--yes`         | boolean    | Skip the `--ssot` confirmation prompt.                                                                            |

`--ssot` is destructive and only applies with `--all`. Use `--dry-run` first.

## roles [#roles]

### Valid forms [#valid-forms-1]

```bash
sb-mig sync roles --all
sb-mig sync roles --all --dry-run
sb-mig sync roles admin editor
sb-mig sync roles admin editor --dry-run
```

### Options [#options-1]

| Flag        | Type       | Effect                                         |
| ----------- | ---------- | ---------------------------------------------- |
| `--all`     | boolean    | Sync every discovered local role schema.       |
| role names  | positional | Sync only the named local role schemas.        |
| `--dry-run` | boolean    | Preview role changes without Storyblok writes. |

## datasources [#datasources]

### Valid forms [#valid-forms-2]

```bash
sb-mig sync datasources --all
sb-mig sync datasources --all --dry-run
sb-mig sync datasources countries cities
sb-mig sync datasources countries cities --dry-run
```

### Options [#options-2]

| Flag             | Type       | Effect                                               |
| ---------------- | ---------- | ---------------------------------------------------- |
| `--all`          | boolean    | Sync every discovered local datasource schema.       |
| datasource names | positional | Sync only the named datasource schemas.              |
| `--dry-run`      | boolean    | Preview datasource changes without Storyblok writes. |

## plugins [#plugins]

### Valid forms [#valid-forms-3]

```bash
sb-mig sync plugins my-awesome-plugin
sb-mig sync plugins my-awesome-plugin --dry-run
```

Run this from a plugin folder that exposes `./dist/export.js`.

### Options [#options-3]

| Flag         | Type       | Effect                                        |
| ------------ | ---------- | --------------------------------------------- |
| plugin names | positional | Sync the named plugins.                       |
| `--dry-run`  | boolean    | Preview plugin sync without Storyblok writes. |

`sync plugins --all` is not implemented. Pass explicit plugin names.

## content [#content]

```bash
sb-mig sync content (--all | --stories | --assets) \
  --from <source> \
  --to <target> \
  --syncDirection <direction>
```

### Selectors [#selectors]

| Selector    | Effect                   |
| ----------- | ------------------------ |
| `--all`     | Sync stories and assets. |
| `--stories` | Sync only stories.       |
| `--assets`  | Sync only assets.        |

### Shared options [#shared-options]

| Flag              | Type    | Effect                                                                             |
| ----------------- | ------- | ---------------------------------------------------------------------------------- |
| `--from`          | string  | Source space ID or local story file name, depending on direction.                  |
| `--to`            | string  | Target space ID or local output file name, depending on direction.                 |
| `--syncDirection` | enum    | One of `fromSpaceToFile`, `fromFileToSpace`, `fromSpaceToSpace`, `fromAWSToSpace`. |
| `--dry-run`       | boolean | Preview supported story/asset sync work without writes.                            |
| `--yes`           | boolean | Skip destructive target-story deletion prompts.                                    |

### Directions [#directions]

| Direction          | Story behavior                                                   | Asset behavior                                                 | Notes                                                                                        |
| ------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `fromSpaceToFile`  | Reads source space stories and writes local story files.         | Reads source space assets and writes local asset backup files. | No target story deletion.                                                                    |
| `fromFileToSpace`  | Reads local story file and writes stories into target space.     | Assets are not implemented for this direction.                 | For stories, non-dry runs prompt, back up target stories, delete target stories, then write. |
| `fromSpaceToSpace` | Reads source space stories and writes into target space.         | Reads source assets and writes into target space.              | Non-dry runs prompt, back up target stories, delete target stories, then write.              |
| `fromAWSToSpace`   | Reads Content Hub/AWS data and writes stories into target space. | Unsupported asset directions log not implemented.              | Intended for Content Hub import flows.                                                       |

### Common examples [#common-examples]

Back up stories and assets to files:

```bash
sb-mig sync content --all \
  --from 12345 \
  --to prod-backup \
  --syncDirection fromSpaceToFile
```

Back up only stories:

```bash
sb-mig sync content --stories \
  --from 12345 \
  --to prod-stories-backup \
  --syncDirection fromSpaceToFile
```

Preview file-to-space story restore:

```bash
sb-mig sync content --stories \
  --from prod-stories-backup \
  --to 67890 \
  --syncDirection fromFileToSpace \
  --dry-run
```

Copy one space into another:

```bash
sb-mig sync content --all \
  --from 12345 \
  --to 67890 \
  --syncDirection fromSpaceToSpace \
  --yes
```

For destructive non-dry content syncs, sb-mig first writes a target story backup
named from the target space, deletes target stories, then recreates stories.

---

# Utility Commands

Source: /docs/reference/utilities

> Copy, discover, migrations, init, and debug command reference.
This page covers commands that do not fit the main sync/migrate/backup groups.

## copy [#copy]

```bash
sb-mig copy stories \
  --from 12345 \
  --to 67890 \
  --source blog/post-1 \
  --destination imported

sb-mig copy stories \
  --from 12345 \
  --to 67890 \
  --source blog \
  --destination imported \
  --with-assets

sb-mig copy assets \
  --from 12345 \
  --to 67890 \
  --all

sb-mig copy assets \
  --from 12345 \
  --to 67890 \
  --asset hero.jpg

sb-mig copy assets \
  --from 12345 \
  --to 67890 \
  --assetFolder Marketing/Heroes

sb-mig copy assets \
  --from 12345 \
  --to 67890 \
  --referenced-by-stories \
  --source blog

sb-mig copy assets \
  --from 12345 \
  --to 67890 \
  --all \
  --dry-run \
  --outputPath sbmig/copy-plans/assets-copy.json
```

`copy stories` copies a source story or folder tree into a target folder.
`copy assets` copies or plans all assets and asset folders. Apply mode writes
asset folders and assets to the target space, finalizes asset uploads, and stores
durable source-to-target manifests under `.sb-mig/copy/<source>/<target>/`.

### Strategies [#strategies]

| Story selector              | Mode       | Behavior                                                                        |
| --------------------------- | ---------- | ------------------------------------------------------------------------------- |
| `--source blog/post-1`      | `subtree`  | Copy one story and attach it under `--destination`.                             |
| `--source blog`             | `subtree`  | Copy the folder story and all descendants under `--destination`.                |
| `--source blog/*`           | `children` | Copy the folder children under `--destination` without copying the folder root. |
| `--source blog --mode self` | `self`     | Copy only the selected story or folder shell.                                   |

### Options [#options]

| Flag                      | Type                             | Effect                                                                                                   |
| ------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `--from`                  | string                           | Source Storyblok space ID. Falls back to configured `spaceId`.                                           |
| `--to`                    | string                           | Target Storyblok space ID. Falls back to configured `spaceId`.                                           |
| `--source`                | string                           | Source story or folder `full_slug`. Use `folder/*` for children-only story copy.                         |
| `--destination`           | string                           | Existing target folder `full_slug` to attach copied stories under. Omit, `/`, or `root` for target root. |
| `--mode`                  | `subtree`, `children`, or `self` | Story copy mode. Defaults to `subtree`; `folder/*` defaults to `children`.                               |
| `--with-assets`           | boolean                          | For `copy stories`, copy referenced assets first and relink copied stories to target asset references.   |
| `--all`                   | boolean                          | Select all assets and asset folders. Use one asset selector family per run.                              |
| `--asset`                 | string                           | Select one source asset by numeric ID, exact Storyblok asset URL, or unique file name. Repeatable.       |
| `--assetFolder`           | string                           | Select one source asset folder by numeric ID or path. Includes descendants and assets in that subtree.   |
| `--referenced-by-stories` | boolean                          | For `copy assets`, select source-space assets referenced by the selected story/folder scope.             |
| `--dry-run`               | boolean                          | Preview paths, manifest-mapped references, and likely conflicts without Storyblok writes.                |
| `--outputPath`            | string                           | Write a JSON dry-run or apply report to a local path.                                                    |

Legacy story-copy aliases still work: `--sourceSpace` for `--from`,
`--targetSpace` for `--to`, `--what` for `--source`, and `--where` for
`--destination`.

`--source` must resolve to an existing story or folder. `--destination` must
resolve to an existing folder in the target space unless it is omitted, `/`, or
`root`. `copy stories` writes copied stories to the target space unless
`--dry-run` is passed and writes story ID/UUID manifests under
`.sb-mig/copy/<source>/<target>/` during apply. It also rewrites mapped asset
and story references in copied target story content after manifests exist.
`copy stories --with-assets` scans the selected stories with source component
schemas, copies referenced assets and their asset-folder ancestors first, then
uses the manifests to relink copied story asset fields to target asset IDs and
filenames. Dry-run output classifies asset references as `mapped`, `planned`, or
`unresolved` and separates reference occurrences from unique assets in the JSON
`assetReferenceSummary`. It also reports foreign Storyblok asset space IDs
detected from asset URLs without trying to copy from those spaces. With
`--with-assets`, referenced assets and folders are marked as `match` when an
existing manifest mapping can be reused. Without `--with-assets`, run
`copy assets` first when copied stories should point to target-space assets.
`copy assets` writes target asset folders/assets unless `--dry-run` is passed.
It supports `--all`, `--asset`, `--assetFolder`, and
`--referenced-by-stories`. `--asset` includes the selected asset's folder
ancestors. `--assetFolder` includes the selected folder, its descendants,
ancestor folders, and assets inside the selected subtree.
`--referenced-by-stories` requires `--source`; it scans the selected story or
folder scope with source component schemas and copies only source-space assets
referenced by that scope. `copy assets` matches by manifest first, then by safe
target folder path or unique target asset file name before creating. `--all`
paginates through source assets and asset folders before planning or applying,
so large spaces are not limited to the first Storyblok Management API page.
Apply mode writes JSONL manifests under `.sb-mig/copy/<source>/<target>/`.

### Resilience and failure reporting [#resilience-and-failure-reporting]

`copy stories` apply mode processes the whole selected tree even when individual
stories fail. If a story update is rejected — for example a `422` when the target
space schema does not allow a component used in the story — the command records
the failure, keeps copying the remaining stories, and then exits non-zero at the
end with a summary of every story that failed. A failing parent keeps its
already-created target shell, so its descendants are still copied under it. One
broken story no longer aborts a large copy.

Failed story updates identify the offending component in place: the component
name, the field it sits in, its parent component, its `_uid`, and its path in the
story content (for example `content.body[3].tabs[1]`), together with the source
and target story IDs, so the exact blok can be located.

### Predicting schema rejections in dry-run [#predicting-schema-rejections-in-dry-run]

`copy stories --dry-run` validates every source component against the target
space component schema before you apply. The JSON report adds a
`componentCompatibility` block and a `summary.componentIssues` count, and the
plan warns when a component would be rejected at write time:

| Warning code                     | Meaning                                                                                 |
| -------------------------------- | --------------------------------------------------------------------------------------- |
| `component_missing_in_target`    | A component used in a source story does not exist in the target space schema.           |
| `component_not_allowed_in_field` | A component sits in a bloks field whose target schema restricts the allowed components. |

Each finding records the component, the reason, the source story `full_slug`, the
field, the parent component, the `_uid`, and the content path. Tag-based field
whitelists are skipped to avoid false positives, and the check is skipped
entirely when the target space returns no components.

## discover [#discover]

### Components [#components]

```bash
sb-mig discover components --all
sb-mig discover components --all --write
sb-mig discover components --all --write --file all-components
```

| Flag      | Type    | Effect                                                  |
| --------- | ------- | ------------------------------------------------------- |
| `--all`   | boolean | Discover all local and external component schema files. |
| `--write` | boolean | Write discovered component names to a local file.       |
| `--file`  | string  | Output file name when `--write` is passed.              |

Without `--write`, the command prints discovered component metadata. With
`--write`, it creates or overwrites the component list file.

### Migrations [#migrations]

```bash
sb-mig discover migrations --all
```

Prints discovered migration config filenames. This command is read-only.

## migrations recognize [#migrations-recognize]

```bash
sb-mig migrations recognize --from 3.4.0
sb-mig migrations recognize --from 3.4.0 --to 4.0.0
```

This command is specific to Backpack upgrade flows. It compares a previous
Backpack version with a target version and prints recommended `migrate content`
and `migrate presets` commands.

| Flag     | Type   | Effect                                                                                                                  |
| -------- | ------ | ----------------------------------------------------------------------------------------------------------------------- |
| `--from` | string | Previous package version. Required.                                                                                     |
| `--to`   | string | Target package version. Optional. Falls back to installed `@ef-global/backpack` dependency version from `package.json`. |

The command reads `applied-backpack-migrations.json` when present and reads
`package.json` when `--to` is omitted. It does not call Storyblok and does not
write files.

## init project [#init-project]

```bash
sb-mig init project \
  --spaceId 12345 \
  --oauthToken xxx \
  --region eu

sb-mig init project \
  --spaceId 12345 \
  --oauthToken xxx \
  --region us \
  --gtmToken GTM-XXXX
```

`init project` creates a local `.env` and updates the Storyblok space preview
domain.

| Flag           | Type                | Effect                                                                    |
| -------------- | ------------------- | ------------------------------------------------------------------------- |
| `--spaceId`    | string              | Storyblok space ID. Required.                                             |
| `--oauthToken` | string              | Management API OAuth token. Required.                                     |
| `--region`     | `eu`, `us`, or `cn` | Storyblok region. Required.                                               |
| `--gtmToken`   | string              | Optional Google Tag Manager token. Defaults to `put-your-gtm-token-here`. |

The generated `.env` includes region URLs, access token, OAuth token, preview
secret, space ID, translation strategy, and GTM ID. The preview domain is set to
`https://localhost:3000/api/preview/preview?secret=<secret>&slug=`.

## debug [#debug]

```bash
sb-mig debug
```

Prints resolved Storyblok config, `sb-mig` version, dependency versions, the
consumer `@ef-global/backpack` version when available, and whether the consumer
package is ESM or CommonJS. It reads local config and package metadata only.
