# CLI (/docs/sdk/cli)

The CLI ships with `@obelism/content` and needs a **management token** of the environment it works on (Settings → API tokens, kind Management) in `CONTENT_MANAGEMENT_TOKEN`; only `types --file` works without one.

## init [#init]

Start a site's repository on a space, in its root:

```sh
export CONTENT_MANAGEMENT_TOKEN=cms_m_…
npx @obelism/content init
```

It:

1. writes the model file, `content/content-model.json`;
2. writes an example of what the delivery API answers for every content type to `content/examples/`, with the [improve block](/docs/guides/ab-tests) while your organization has Improve on;
3. installs `@obelism/content` with your package manager;
4. adds the token variables (`CONTENT_DELIVERY_TOKEN`, `CONTENT_PREVIEW_TOKEN`, `CONTENT_MANAGEMENT_TOKEN`) to `.env.example`, empty.

| Option           |                                                               |
| ---------------- | ------------------------------------------------------------- |
| `--dir <path>`   | Where the files go (default `content`)                        |
| `--url <origin>` | The CMS (default `$CONTENT_URL`, else content.obelism.studio) |
| `--skip-install` | Don't install the SDK                                         |

Agents get the same steps from the `getProjectSetup` MCP tool, and run `init` rather than writing the files themselves.

## model [#model]

Keep the model file in Git in step with an environment ([the model in Git](/docs/guides/model-in-git)):

```sh
npx @obelism/content model pull    # write content/content-model.json
npx @obelism/content model plan    # what applying would change, and what stands in the way
npx @obelism/content model apply   # apply it, all or nothing
```

| Option           |                                                                  |
| ---------------- | ---------------------------------------------------------------- |
| `--file <path>`  | The model file (default `content/content-model.json`)            |
| `--url <origin>` | The CMS (default `$CONTENT_URL`, else content.obelism.studio)    |
| `--prune`        | `plan` and `apply` also delete content types the file leaves out |

`plan` and `apply` exit with 1 when the file has problems, listing each one; `apply` then changes nothing. In GitHub Actions they also write the plan to the job summary.

## types [#types]

Since `@obelism/content` 0.6.0. Write a TypeScript type per content type to `content/content-types.ts`:

```sh
npx @obelism/content types                                    # from the environment's model
npx @obelism/content types --file content/content-model.json  # from the model file, no token needed
```

Page and data types become `Entry<{ …fields }, 'apiId'>`, block types `Block<{ …fields }, 'apiId'>`, named after their API id (`blogPost` → `BlogPost`), with `ContentEntry` and `ContentBlock` as the unions of them. Each field has the type the delivery API answers with its defaults (one locale, rich text as a document), or `null` when it is empty:

| Field                    | Type                                                                 |
| ------------------------ | -------------------------------------------------------------------- |
| text, textarea, datetime | `string`                                                             |
| number, boolean          | `number`, `boolean`                                                  |
| select                   | its options' values (a list when multiple)                           |
| asset                    | `Asset` (a list when multiple)                                       |
| reference                | the allowed entry types or their `EntryStub`s (a list when multiple) |
| link                     | `Link`                                                               |
| blocks                   | a list of the allowed block types and the improve block              |
| rich text                | `RichTextDocument`                                                   |
| JSON                     | `unknown`                                                            |

A localized field is typed as one locale's value. Pass the types to the client, e.g. `client.getPage<BlogPost>('blog/hello')`, and run `types` again when the model changes.

| Option           |                                                               |
| ---------------- | ------------------------------------------------------------- |
| `--dir <path>`   | Where `content-types.ts` goes (default `content`)             |
| `--file <path>`  | Read this model file instead of the environment's model       |
| `--url <origin>` | The CMS (default `$CONTENT_URL`, else content.obelism.studio) |
