---
title: CLI
description: Every odori command, its flags, and what it writes.
---

The CLI ships as `@odori/cli` and installs a `odori` binary.

```bash
pnpm add -D @odori/cli
pnpm odori --help
```

## `odori dev`

Discovers the project, regenerates `.odori/`, and starts Studio.

```bash
odori dev --port 4300
```

Studio watches `videos/` and reloads when a `video.tsx`, `*.preview.tsx`, or
brand module appears or disappears. The dev server also exposes the endpoints
Studio uses to request a still or an export.

Studio opens in the default browser when the shell is interactive. Pass
`--no-open`, set `open: false` in `odori.config.ts`, or set `ODORI_OPEN=0` to
start without it. CI and piped output never open a browser.

```bash
odori dev --no-open
```

`docsUrl` in `odori.config.ts` sets where Studio's documentation link points,
and defaults to the local docs site at `http://localhost:4321`.

## `odori init`

Adds `videos/layout.tsx`, a first `videos/launch/video.tsx`, and
`odori.config.ts` to an existing project. Existing files are never overwritten.

## `odori new <name>`

Generates `videos/<name>/video.tsx` with static metadata and a JSX timeline. It
wires the root layout when `videos/layout.tsx` exists.

## `odori add <components...>`

Copies registry component source and its preview fixture into
`videos/components/`, resolves registry dependencies, and records provenance in
`.odori/components.json`.

```bash
odori add @odori/title-reveal @odori/end-card
odori add @odori/terminal --force
```

A component you have edited locally is kept, with a warning, unless `--force`
is passed.

## `odori registry`

Prints the catalog grouped by family, with aspect ratios, minimum duration, and
reduced-motion behavior for each component.

## `odori diff [components...]`

Compares installed component source with the version that was installed and
with the version the registry ships today. Each component is reported as
`up to date`, `modified locally`, `update available`, or `modified locally and
updated upstream`.

```bash
odori diff
odori diff terminal --full
```

## `odori update [components...]`

Applies upstream changes to components you have not edited. A component that is
both edited locally and changed upstream is left alone until you review it with
`odori diff` and pass `--force`.

## `odori list`

Prints discovered video IDs, formats, durations, source files, and the number
of component previews.

## `odori inspect <id>`

Resolves the layout, compiles the timeline in a browser, runs `prepare.ts`, and
freezes a manifest.

```bash
odori inspect launch
odori inspect launch --json --input '{"headline":"Ship it."}'
```

## `odori still <id>`

Renders one deterministic frame.

```bash
odori still launch --frame 120 --output out/hero.png
```

## `odori test [id]`

Validates contracts and representative frames for one video or all of them.
It fails when default props do not satisfy the schema, when a declared duration
disagrees with the compiled scene total, when a frame is blank, when content
escapes the canvas, or when text is smaller than 20px at a 1080p reference.

## `odori export <id>`

Freezes a manifest, records a job under `.odori/builds/`, renders every frame
through the readiness handshake, mixes the audio track, and encodes an MP4 with
FFmpeg. Frames are captured by several browser workers in parallel.

```bash
odori export launch --output out/launch.mp4
odori export launch --concurrency 8 --preset slow
odori export --retry job-88e09129a0-msw17lz9
```

A retry replays the frozen manifest, so it never re-resolves inputs or reruns
`prepare.ts`.

## `odori jobs`

Lists recorded export jobs with status, attempts, and output path.

```text
job-88e09129a0-msw17lz9  launch  ready  attempts 2  out/launch.mp4
```

## Shared flags

| Flag | Purpose |
| --- | --- |
| `--input '<json>'` | Serializable input validated by the video schema |
| `--output <path>` | Output path for `still` and `export` |
| `--force` | Replace locally modified component source |
| `--json` | Machine-readable output for `inspect` |
| `--concurrency <n>` | Parallel render workers for `export` |
| `--preset <name>` | x264 preset for `export`, default `medium` |
| `--retry <job id>` | Re-run a recorded job from its frozen manifest |
| `--full` | Print the diff body in `diff` |

## Requirements

Rendering needs Chrome or Chromium and FFmpeg. Odori looks for a browser at the
usual macOS and Linux locations, at `chromePath` in `odori.config.ts`, or at
`ODORI_CHROME`.
