# Comvi CLI

Install @comvi/cli, authenticate with an API key, and sync translation files or generate TypeScript types from your terminal and CI.

Canonical: https://comvi.io/docs/cli

`@comvi/cli` moves translations between your repository and your Comvi project and
generates a TypeScript declaration from the project's keys.

## Install
**npm**

```bash
npm install -D @comvi/cli
```

**pnpm**

```bash
pnpm add -D @comvi/cli
```

**yarn**

```bash
yarn add -D @comvi/cli
```

**bun**

```bash
bun add -d @comvi/cli
```

Run it as `npx comvi <command>` once it is a dev dependency, or as `npx --package @comvi/cli comvi <command>` with nothing installed.

## Authenticate

`Terminal`

```bash
export COMVI_API_KEY=comvi_xxxxxxxxxxxxxxxx
npx comvi pull
```

Every command reads `COMVI_API_KEY`. The key selects the project, so there is no project
id to configure. Create one under [API keys](/docs/platform/api-keys). The CLI also loads
the nearest `.env` file before each command, walking up from the working directory and
stopping at the first `package.json`, so `COMVI_API_KEY=...` in `.env` works with no
wrapper. Real environment variables always win over the file.

| Variable | Effect |
| --- | --- |
| `COMVI_API_KEY` | API key. Overrides `apiKey` in the config file |
| `COMVI_API_BASE_URL` | Overrides `apiBaseUrl` in the config file |
| `COMVI_NO_ENV=1` | Skip auto-loading `.env`, same as `--no-dotenv` |
| `COMVI_DEBUG=1` | Print to stderr which `.env` was loaded and how many entries it added or skipped |

## Config file

`Terminal`

```bash
npx comvi init
```

`.comvirc.json`

```json
{
"apiBaseUrl": "https://api.comvi.io",
"outputPath": "src/types/i18n.d.ts",
"strictParams": true,
"translationsPath": "./src/locales",
"fileTemplate": "{namespace}/{languageTag}.json",
"format": "json",
"locales": ["en", "de"],
"namespaces": ["common", "auth"],
"push": { "forceMode": "ask" },
"pull": { "emptyDir": false }
}
```

`comvi init` writes `.comvirc.json` in the current directory without `locales` or
`namespaces`; add those by hand. It does write `push` and `pull` with their defaults,
`"forceMode": "ask"` and `"emptyDir": false`. The file is safe to commit because the
key stays in the environment. Commands find it by walking up from the working directory
and stopping at the first `package.json`, so in a monorepo each package needs its own file
or an explicit `--config ../../.comvirc.json`.

| Field | Default | Meaning |
| --- | --- | --- |
| `apiKey` | `$COMVI_API_KEY` | API key. Prefer the environment variable |
| `apiBaseUrl` | `https://api.comvi.io` | Comvi API base URL |
| `outputPath` | `src/types/i18n.d.ts` | Where `comvi typegen` writes the declaration |
| `strictParams` | `true` | `false` makes every interpolation parameter optional |
| `augment` | package the app declares | Module the declaration augments, for a private wrapper package. See [Type-check translation keys](/docs/i18n/type-safety) |
| `translationsPath` | `./src/locales` | Directory for local translation files |
| `fileTemplate` | `{namespace}/{languageTag}.json` | File layout for pull and push. With the default template the default namespace lives at the root as `{languageTag}.json`; a custom template is matched literally. Must contain `{languageTag}` and `{namespace}` once each and end in `.json` |
| `format` | `json` | Translation file format. Only `json` exists |
| `locales` | all project locales | Restrict pull and push to these BCP-47 codes (`pt-BR`, `zh-Hans`). `--locale` replaces the list |
| `namespaces` | all namespaces | Restrict pull and push to these namespaces. `--ns` replaces the list |
| `push.forceMode` | `ask` | Conflict handling for `comvi push`: `override`, `keep`, `ask` or `abort` |
| `pull.emptyDir` | `false` | Clear the pulled locales and namespaces after a successful fetch |

`strictParams` and `pull.emptyDir` must be JSON booleans: the string `"false"` fails
validation with exit `4` before any command opens a connection. An empty `locales` or
`namespaces` list, a duplicate, or a non-string entry fails the same way. So does a field the
config does not have: the legacy `languages` and `defaultNsName` are rejected by name, with
`locales` named as the replacement for the first; the default namespace comes from the
project.

### `comvi init` options

| Flag | Default | Meaning |
| --- | --- | --- |
| `-k, --api-key <key>` | none | Write the key into the file. Prints a warning: arguments show up in process listings and shell history |
| `-u, --api-url <url>` | `https://api.comvi.io` | `apiBaseUrl` to write. A malformed URL exits `4` and writes nothing |
| `-o, --output <path>` | `src/types/i18n.d.ts` | `outputPath` to write |
| `--no-strict-params` | off | Write `strictParams: false` |
| `--translations-path <path>` | `./src/locales` | `translationsPath` to write |
| `--file-template <template>` | `{namespace}/{languageTag}.json` | `fileTemplate` to write |
| `--force` | off | Overwrite an existing `.comvirc.json` |

An existing file is left untouched and the command exits `1`. `--force` rewrites the
whole file, dropping any `locales` and `namespaces` and resetting `push` and `pull` to their
defaults. When a key
is set, `init` validates it against the platform; a failed validation is a warning and the
file is still written.

## Commands

| Command | Purpose |
| --- | --- |
| [`comvi pull`](/docs/cli/pull) | Download translations from the project to local JSON files |
| [`comvi push`](/docs/cli/push) | Upload local translation files to the project |
| [`comvi typegen`](/docs/cli/typegen) | Generate a TypeScript declaration from the project's keys |

## Global flags

| Flag | Meaning |
| --- | --- |
| `-V, --version` | Print the installed `@comvi/cli` version |
| `-h, --help` | Print usage for the program or one command |
| `--dotenv <path>` | Load one `.env` file instead of auto-discovery. A missing file exits `4` |
| `--no-dotenv` | Skip auto-loading `.env` |

The flag is `--dotenv`, not `--env-file`: Node claims `--env-file` across the whole command
line and exits `9` on a missing file before the CLI starts.

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Success |
| `1` | General error: missing `.comvirc.json`, auth failure, network error, failed generation |
| `2` | `comvi typegen --check` only: the check could not run because the API was unreachable, timed out, rejected the key or returned something unusable |
| `4` | Validation error: missing API key, malformed config JSON or field, a translation file with a non-string value or a key defined both flat and nested, a locale or namespace the project does not have, an unknown `--force-mode`, or a `--dotenv` file that does not exist |

A missing `.comvirc.json` exits `1`; an invalid one exits `4`.

## Next steps

- [comvi pull](/docs/cli/pull): download translations, filters and `--empty-dir`.
- [comvi push](/docs/cli/push): upload translations and resolve conflicts.
- [comvi typegen](/docs/cli/typegen): generate types, watch mode and `--check` in CI.
- [CI/CD integration](/docs/platform/recipes/ci-cd-integration): full pipeline examples.
