> ## Documentation Index
> Fetch the complete documentation index at: https://developer.dittowords.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are Ditto's developer docs. Ditto is a single source of truth for product copy across design, review, localization, and code.
> The first step on any codebase is a scan: https://developer.dittowords.com/get-started/scan-your-repo.md. That page also covers reviewing the style guide Ditto drafts from the scan. After it, the follow-on guides are Connect Figma files and Set up localization (under /guides/) and Connect the Ditto MCP (the Setup page of the MCP reference).
> Two different MCP servers: the Ditto MCP server at https://api.dittowords.com/v2/mcp gives an agent a workspace's text and style guide rules; the documentation MCP server at https://developer.dittowords.com/mcp searches these docs.
> Prefer logging in with a Ditto account (npx -y @dittowords/cli@latest login; OAuth in MCP clients) over API keys. API keys are for CI and headless clients only.
> Commands are complete as written and use npx -y @dittowords/cli@latest; nothing needs a global install.

# Ditto CLI commands

> Reference for every Ditto CLI command and its options.

All commands run through `npx`, so nothing needs to be installed globally:

```bash theme={null}
npx -y @dittowords/cli@latest <command>
```

The examples below omit `-y` and `@latest` for brevity. Every command that talks to Ditto needs a credential: a saved browser login or the `DITTO_TOKEN` environment variable. See [Install and log in](/cli-reference/authentication).

## scan

Analyzes a path for user-facing text and sends the candidates to Ditto, where you review them and turn them into library components. This is the first step of setting up Ditto on a codebase; the walkthrough is [Scan your repo](/get-started/scan-your-repo).

```bash theme={null}
npx @dittowords/cli scan [path]
```

`[path]` is the file or directory to scan; it defaults to the current directory. The CLI extracts candidate strings, uploads them, and prints a link to the review page:

```text theme={null}
Scan initiated! Visit https://app.dittowords.com/scan/<id> to view progress and see results.
```

<Info>
  **Requires Node.js 20 or higher** and a credential. Without a saved login or `DITTO_TOKEN`, `scan` stops and prompts for an API key. `--list-directories` and `--local` need no credential.
</Info>

**What is uploaded.** For each candidate: the text, file path and line, how it was detected (markup text, attribute value, localization resource value, or other), the i18n key and locale if it came from a translation file, surrounding source lines, and nearby identifiers. In a Git checkout with a remote, also the remote's host and path, commit, branch, scanned paths, and file renames since the last scan. Never credentials.

**Requires a clean default branch.** Scan records the commit and branch it read, and the code links Ditto builds point at them. To keep those links on code your team shares, `scan` runs only from a clean checkout of your repository's default branch. It stops, without uploading, on uncommitted changes, a non-default branch, or a detached `HEAD`:

```text theme={null}
On branch "my-feature", but this repo's default branch is "main". Check out "main" and scan again.
```

The default branch comes from `origin/HEAD`, falling back to a local `main` or `master`. Because that reference is set when you clone and doesn't update on its own, a mismatch is confirmed against your remote first, so a branch renamed after you cloned won't block you. Scanning outside a Git repository is unaffected.

**Preview counts by directory.** Extract candidates, print a per-directory count, and exit without uploading:

```bash theme={null}
npx @dittowords/cli scan . --list-directories
```

```text theme={null}
Strings by directory (rolled up), under .:

  1,284  .  (whole scan)
    382  packages
    382    packages/ui
    902  src
    418    src/components
    344    src/lib
    140    src/pages
```

Counts are rolled up, so each directory includes its subdirectories. Directories are listed in tree order, indented by depth, with the whole-scan total first.

**Write candidates locally instead of uploading.**

```bash theme={null}
npx @dittowords/cli scan [path] --local --out-dir ./out
```

Writes `<out-dir>/candidates.ndjson`, one JSON object per line. Add `--prefix <prefix>` to prefix the file name. Each object has `value_raw`, `snapshot_text`, `detection_kind` (`markup_text`, `markup_attr`, `resource_value`, `other`), `location` (`file`, `line`, `column`), `language`, `framework`, `i18n_key`, `locale_key`, `source_context`, `context_identifiers`, and `usage_evidence`.

**Plan limits.** Trial workspaces can classify up to 1,000 strings in total across all of their scans; paid plans have no cap. Re-scanning a directory does not count twice. If a scan would exceed the remaining budget, the CLI stops before uploading and suggests the largest subdirectories that fit (up to 10):

```text theme={null}
This scan found 1,234 strings, but your Trial plan allows 1,000 scanned strings.

Scan one of these subdirectories instead. Each fits within your plan:

  npx @dittowords/cli scan src/components   (418 strings)
  npx @dittowords/cli scan src/lib          (344 strings)
  npx @dittowords/cli scan src/pages        (140 strings)

Run `npx @dittowords/cli scan . --list-directories` for the full breakdown.
```

**Options**

| Flag | Description |
| - | - |
| `--list-directories` | Print the number of candidate strings per directory and exit, without uploading. |
| `--local` | Write the extracted candidates to disk instead of uploading them. Requires `--out-dir`. |
| `--out-dir <dir>` | Directory to write candidates to when using `--local`. |
| `--prefix <prefix>` | Prefix for the names of the output files. |

## pull

Fetches the latest text from Ditto and writes string files to disk, as described by your [config file](/cli-reference/configuration).

```bash theme={null}
npx @dittowords/cli pull
```

Files are written to the output directory in your config (`./ditto` by default). For the structure of the generated files, see [Files](/cli-reference/files). Because `pull` updates files in your repo, you manage the result like any other change: commit it, open a pull request, or run it in CI before a build.

**Custom config location.** By default `pull` reads `./ditto/config.yml` relative to where you run it. Use `-c` / `--config` for another path, or set the `DITTO_PROJECT_CONFIG_FILE` environment variable:

```bash theme={null}
npx @dittowords/cli pull -c ./path/to/config.yml
```

**Request metadata.** `-m` / `--meta` attaches arbitrary key-value data to the API requests, which shows in Ditto's activity history:

```bash theme={null}
npx @dittowords/cli pull -m githubActionRequest:true trigger:manual
```

## login

Logs in to Ditto through your browser and saves the session to `$HOME/.config/ditto`.

```bash theme={null}
npx @dittowords/cli login
```

A browser tab opens for you to log in and approve access. Later commands reuse the session and renew it as needed. If `DITTO_TOKEN` is set, the CLI keeps using that API key instead, because an environment variable outranks a saved session; see [credential precedence](/cli-reference/authentication#credential-precedence).

<Info>`login` and `logout` require version `5.7.0` or higher.</Info>

## logout

Forgets the session saved on this machine and revokes it with Ditto.

```bash theme={null}
npx @dittowords/cli logout
```

Reports back if `DITTO_TOKEN` is still set, because commands would keep working with that API key.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.