> From the [Strife developer docs](https://strife.app/docs/reference/cli/commands). Every page is listed in [llms.txt](https://strife.app/docs/llms.txt).

# Commands

Full reference for every strife command — syntax, flags, authentication requirements, and what each one does.

Run `strife --help` at any time for a summary of the same information.

### Global behavior

* `--help` / `-h` and `--version` / `-v` are only recognized **before** the command name (`strife --help` works; `strife push --help` does not — it's passed through to `push`, which ignores it).
* `--json` is **not** a global flag. Pass it after the command name on commands that support it (`strife whoami --json`), not before.
* `--non-interactive` suppresses prompts and browser-open attempts, for scripts; some commands require an existing link when run this way, since there's nothing to prompt for otherwise. It does not sign you in: commands that talk to Strife still need a `strife login` session, which a CI runner cannot get today — see [Non-interactive use](https://strife.app/docs/reference/cli/authentication-and-linking.md#non-interactive-use).
* Running an unrecognized command prints an error and exits with a non-zero status.
* Set `STRIFE_NO_BANNER=1` to suppress the startup banner (useful when wrapping `strife` in scripts).

### Authentication requirements by command

| Command                                 | Requires login               | Requires link                 |
| --------------------------------------- | ---------------------------- | ----------------------------- |
| `login`                                 | — (that's its job)           | no                            |
| `logout`                                | no                           | no                            |
| `whoami`                                | yes                          | no (degrades gracefully)      |
| `link`                                  | yes                          | — (that's its job)            |
| `push`                                  | yes                          | yes                           |
| `upload`                                | yes                          | yes                           |
| `setup` (bare `strife` / `strife .`)    | bootstraps sign-in if needed | no (establishes it)           |
| `teams ls` / `teams rm`                 | yes                          | no                            |
| `domains` / `domains set`               | yes                          | yes                           |
| `folders create`                        | yes                          | only if `--team` isn't passed |
| `typegen generate` / `typegen validate` | no                           | no — fully offline            |

## `strife login`

```
strife login [--api-url <url>] [--force]
```

Authenticates via an OAuth 2.0 Device Authorization Grant. The CLI does not open a browser for you — it prints a URL and a code to visit. If you already have a valid session, `login` does nothing unless you pass `--force` (useful for switching accounts or workspaces, since a login session is scoped to one workspace for its lifetime).

| Flag              | Description                                                                           |
| ----------------- | ------------------------------------------------------------------------------------- |
| `--api-url <url>` | Override the API base URL. Defaults to `$STRIFE_API_URL` or `https://api.strife.app`. |
| `--force`         | Re-authenticate even if a valid session already exists.                               |

## `strife logout`

```
strife logout [--json]
```

Revokes the current session server-side on a best-effort basis and always clears local credentials — it exits successfully even if the machine was already logged out, or if the server-side revoke call fails. Does not remove any directory's `.strife/project.json` link; a previously linked directory still shows as linked, it just can't authenticate until you log in again.

## `strife whoami`

```
strife whoami [--json]
```

Prints the signed-in user, the active workspace/team (read from the current directory's link, not from the session token), and when the session expires.

## `strife link`

```
strife link [--workspace <id|slug>] [--team <id|key>] [--force]
```

Links the current directory to a team, writing `.strife/project.json`. The workspace is fixed by your login session — there's no workspace picker here, only a team picker. If the target workspace has no teams yet, `link` fails; create one first (see [bootstrapping a new project](https://strife.app/docs/reference/cli.md#bootstrapping-a-brand-new-project)).

| Flag                     | Description                                                                                                  |
| ------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `--workspace <id\|slug>` | Assert which workspace you expect to link into. Errors if it doesn't match your session's workspace.         |
| `--team <id\|key>`       | Pick a team non-interactively. Required in `--non-interactive` mode if the workspace has more than one team. |
| `--force`                | Relink even if the directory is already linked.                                                              |

`.strife/project.json` contains only non-secret identifiers (workspace/team IDs and names) and is safe to commit.

## `strife push`

```
strife push [--config <path>] [--dry-run] [--force] [--prune] [--allow-missing-ids] [-y | --yes]
```

Compiles the schema files described by `strife.config.ts` (see [Schemas](https://strife.app/docs/reference/cli/schemas.md)) and deploys them to the linked team: templates are upserted by id, followed by deployment of the content index and, if configured, the team's localization settings. Before reading the schemas it loads `.env.local` and `.env` from the current directory into the environment (never overriding a variable the shell already set), so a schema can read `process.env`; in a directory linked to a team, the lines prefixed with the team's key (`ACME_BLOG_PAGE_ID`) stand in for the plain name — see [Placing pages under a specific page](https://strife.app/docs/reference/cli/schemas.md#placing-pages-under-a-specific-page). They are removed again once the schemas are loaded, so nothing in `.env` changes how the CLI itself talks to Strife.

| Flag              | Description                                                                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `--config <path>` | Path to a config file other than the project root's `strife.config.ts` — useful in monorepos.                                         |
| `--dry-run`       | Print the compiled output without deploying anything. Its only request is a read of the team's current templates, to warn about `baseProperties` flags the push would turn off; if that read fails, the dry run still completes. |
| `--force`         | Skip the optimistic-concurrency check (see below) and push regardless of whether the team's templates changed since you last checked. |
| `--prune`         | Also archive templates that exist on the team but aren't in this push (see below). Off by default.                                    |
| `--allow-missing-ids` | Push even when a `from: { id: process.env.X }` entry reads an unset variable: the id is left out, with a warning. Without it such a push is refused (also under `--dry-run`, after the compiled output), so a template cannot silently lose its parent. |
| `-y`, `--yes`     | Skip the confirmation prompt. Required in `--non-interactive` mode.                                                                   |

**Push upserts by id; it does not delete by omission unless you ask it to.** Every deploy is checked against the team's current state via a version fingerprint; if something else changed the team's templates since your last check, `push` fails with a conflict rather than silently overwriting (`--force` bypasses this). By default, templates that exist on the team but aren't present in your local schema are left untouched — a teammate working from a different branch, with a different subset of schemas, can push without affecting templates they simply don't have locally yet. Pass `--prune` to opt into the old full-replace behavior instead: templates missing from the push are archived, not deleted, so they can be restored by pushing them again. `--prune` is meant for a deliberate, occasional cleanup sweep — run from your default branch, coordinated with anyone else who might be pushing — not as part of routine day-to-day pushes.

> [!WARNING]
> Choose type `name`s that are unlikely to collide with what teammates are independently defining. Two different local schemas that both resolve to the same template — for example, two people separately defining a type named `Home` — deploy to the same underlying document, which can produce confusing results if pushed independently rather than as one shared schema.

## `strife upload`

```
strife upload <file...> [--folder <id>] [--workspace <id>] [--name <name>]
```

Uploads one or more local files to the linked team's media library, directly to storage (not proxied through the Strife API). All files are validated up front — a missing or unsupported file anywhere in the batch aborts before anything uploads. Video files are rejected client-side (and server-side) by extension.

| Flag               | Description                                                    |
| ------------------ | -------------------------------------------------------------- |
| `--folder <id>`    | Target folder. Defaults to the team's root.                    |
| `--workspace <id>` | Attribute the upload to a workspace other than the linked one. |
| `--name <name>`    | Display name. Only applies when uploading a single file.       |

## `strife` / `strife .` (setup)

No arguments — see [bootstrapping a brand-new project](https://strife.app/docs/reference/cli.md#bootstrapping-a-brand-new-project) in the overview.

## `strife teams`

```
strife teams ls
strife teams rm <id|key> [-y | --yes]
```

`ls` lists the teams in your current workspace. `rm` **permanently** deletes a team — its database, storage, and certificates — after a confirmation prompt (skip with `--yes`).

## `strife domains`

```
strife domains
strife domains set <url>
```

Shows or sets the linked team's site origin. `set` requires an absolute `http(s)` URL.

## `strife folders create`

```
strife folders create <name> [-p | --parent <id>] [--team <id|key>] [--json]
```

Creates a media library folder. Uses the linked team unless `--team` is given.

## `strife typegen`

```
strife typegen generate [--config <path>] [-o | --output <path>]
strife typegen validate [--config <path>] [-o | --output <path>] [--allow-missing-ids]
```

See [Typegen](https://strife.app/docs/reference/cli/typegen.md) for the full reference — both subcommands are fully offline and require neither authentication nor a linked project. Like `push`, both load `.env.local` and `.env` from the current directory before reading the schemas, and in a directory linked to a team they read that team's prefixed lines the same way.
