# CLI — Client

> storyshelf init, create, upload, build, doctor, whoami, retry, purge, protect — client-side commands.

Source: https://storyshelf.js.org/guides/cli/client/

The client CLI talks to a running StoryShelf server over `/api/v1` and carries no Playwright or server dependencies. Run it with your package manager’s runner:

```sh
npx storyshelf --help
```

> **Package reference:** For install and programmatic use see [@storyshelf/cli](https://storyshelf.js.org/packages/cli/).

## `storyshelf init`

Initialize the current Storybook with `.storybook/storyshelf.json` (client config). Fails if `.storybook/main.*` is not found.

```bash
storyshelf init --url http://localhost:3000 --slug my-design-system --build-dir storybook-static
# or with prompts:
storyshelf init
# ? Server URL? http://localhost:3000 (detected React-Vite • 1 addons)
# ? Project slug? my-design-system
# also: --build-dir, --build-command, --build-script-name, --skip, -c/--config
```

Writes `.storybook/storyshelf.json: { "slug": "...", "url": "...", "buildDir": "...", "skip": "..." }` (no token, see [Configuration](https://storyshelf.js.org/guides/config/)). Token stays in `STORYSHELF_TOKEN` env / `--token`.

## `storyshelf create`

Create a project on the server (requires site-admin token) and write `.storybook/storyshelf.json`.

```bash
storyshelf create --url http://localhost:3000 --name "My Design System" --token $STORYSHELF_ADMIN_TOKEN
# also reads STORYSHELF_ADMIN_TOKEN / ADMIN_TOKEN env if --token omitted
```

Prints `Project slug: ...` and `CI token: ...`.

## `storyshelf build`

Build the Storybook output without uploading — same `buildDir`/`buildCommand` resolution as `upload`, but no server contact.

```bash
storyshelf build
storyshelf build --force-build
storyshelf build --build-dir dist-storybook --build-command "nx run app:build-storybook"
```

Prints `Build ready: <dir>` and fails fast when the output lacks `index.json`.

## `storyshelf upload`

Build (optionally), zip, and upload a Storybook build for capture.

```bash
# explicit flags
storyshelf upload \
  --url http://localhost:3000 \
  --slug my-design-system \
  --token shelf_xxx \
  --sha "$GITHUB_SHA" \
  --branch "$GITHUB_REF_NAME"

# with .storybook/storyshelf.json present (no args defaults to upload)
storyshelf
storyshelf upload --token shelf_xxx --sha $GITHUB_SHA --branch main
```

| Flag                              | Description                                                                                                             |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `--url`                           | Server URL (or file / `STORYSHELF_URL`)                                                                                 |
| `--slug`                          | Project slug (or file / `STORYSHELF_SLUG`)                                                                              |
| `--token`                         | Project API token (`Authorization: Bearer`, or `STORYSHELF_TOKEN`)                                                      |
| `--sha`                           | Revision SHA (flag, CI env, local git HEAD, or synthesized `local-*`)                                                   |
| `--branch`                        | Baseline namespace (flag, CI env, local git branch, or `local`)                                                         |
| `--build-dir` / `-d`              | Built Storybook directory (default `storybook-static`)                                                                  |
| `--config` / `-c`                 | Config file path (default `.storybook/storyshelf.json`)                                                                 |
| `--build-command`                 | Custom build command (mutually exclusive with `--build-script-name`)                                                    |
| `--build-script-name` / `-b`      | npm script to build (default `build-storybook`)                                                                         |
| `--force-build`                   | Force rebuild even if `buildDir` exists                                                                                 |
| `--skip`                          | Glob to skip upload (e.g. `"main"`)                                                                                     |
| `--message`                       | Build message                                                                                                           |
| `--author-name`, `--author-email` | Author attribution                                                                                                      |
| `--label key=value`               | Attach a build label (repeatable)                                                                                       |
| `--full`                          | Disable affected capture for this upload (render every story)                                                           |
| `--untraced <glob>`               | Exclude matching files from affected tracing (repeatable, e.g. `"**/*.generated.ts"`; unions with `untraced` in config) |
| `--stats-file <path>`             | Bundler stats file (default `<buildDir>/preview-stats.json`)                                                            |
| `--dry-run`                       | Validate/build but send no requests                                                                                     |

Affected capture is on by default: the CLI traces `git diff baseline..HEAD` through the build’s dependency graph, posts the affected set, and prints a summary (`Affected capture: rendering 3 stories (baseline abc1234)`). Anything it cannot prove unchanged renders anyway — see [Affected capture](https://storyshelf.js.org/concepts/affected-capture/). `STORYSHELF_FULL=1` and `"affectedOnly": false` opt out the same way as `--full`.

> **Note:**
>
> The CLI does **not** run Playwright. It streams the zipped static build to the server; the server renders and diffs asynchronously (`202`).

**Servers without `testing`.** An instance that [hosts Storybooks only](https://storyshelf.js.org/concepts/hosting-only/) reports `testing: false` when the build is created. `storyshelf upload` then skips the affected-capture computation, uploads as usual, and ends with `Testing is off on this server: the Storybook is published, but no captures or visual checks will run.`

**Large Storybooks on hosts without a job consumer.** A zip over the server’s `maxInlineUnzipSize` is normally published by a background job. A serverless host runs no consumer, so it answers `413` with a message pointing to the file-by-file route (`/dedup`, `/content`, `/manifest`). `storyshelf upload` handles that by itself: it prints `Zip is over the server's inline publish limit; uploading file by file instead`, hashes the build directory, uploads only the files the server lacks, and posts the manifest, which completes publishing. A `413` for `maxUploadBytes` is not retried this way; raise the limit instead.

## `storyshelf doctor` / `whoami`

```bash
storyshelf doctor
# ✓ Storybook setup found
# ✓ Config loaded (slug "my-design-system")
# ✓ Connection: http://localhost:3000 / my-design-system (token present)
# ✓ Server reachable
# ! Build output missing — upload would run: npm run build-storybook -- --output-dir storybook-static
# ✓ Affected capture ready (history and stats available)

storyshelf whoami
# Server: http://localhost:3000
# Project: My Design System (my-design-system)
```

## `storyshelf retry` / `purge`

```bash
storyshelf retry --url http://localhost:3000 --slug my-design-system --build-id <id> [--token $STORYSHELF_TOKEN]
storyshelf purge --url http://localhost:3000 --token $STORYSHELF_ADMIN_TOKEN
```

`retry` re-runs capture without a new revision. `purge` triggers retention GC (normally scheduled).

## `storyshelf protect`

Print, validate and (only with `--apply`) write the branch rule that makes StoryShelf gate merges. It talks to GitHub or GitLab directly with an admin token; it does not contact your StoryShelf server.

```bash
storyshelf protect --provider github --repo acme/widgets [--branch main] [--prefix storyshelf] [--gate testing|review] [--token $GITHUB_TOKEN] [--apply]
storyshelf protect --provider gitlab --repo group/project [--host https://gitlab.example.com] [--token $GITLAB_TOKEN] [--apply]
```

It always prints the required check names (`<prefix>/testing`, or `<prefix>/review` with `--gate review`) and the manual steps. With a token (`--token`, or `GITHUB_TOKEN` / `GITLAB_TOKEN`) it also compares them with the branch’s rule: the exit code is `1` when something is missing and nothing was written (dry run, the default). `--apply` adds only the missing part. See [Let StoryShelf set the rule](https://storyshelf.js.org/guides/merge-gate/#let-storyshelf-set-the-rule) for what each host supports.

See [Configuration](https://storyshelf.js.org/guides/config/) for file/env precedence and [Labels](https://storyshelf.js.org/concepts/labels/) for `persistent` tagging.
