# CLI Command Reference

> Every stringlane command and flag, what each one writes, and the exit code it sets. Transcribed from the shipped binary at version 0.3.1.

Source: https://stringlane.app/docs/reference/cli
Last updated: 2026-09-16

Every command takes an optional `[project]` argument, which defaults to the current directory.

This page is transcribed from `stringlane --help` and each command's own `--help` at version `0.3.1`. Where it disagrees with your terminal, your terminal is right: run `stringlane <command> --help`.

## Reading commands

These write nothing inside your project: not a lock file, not a re-saved locale file, not a byte. `check` and `scan` do keep a source-scan cache between runs, in StringLane's own state directory under `STRINGLANE_STATE_HOME` — outside your repository, so there is nothing to gitignore.

### `stringlane inspect [project]`

Reports what the project holds: every locale, how complete it is, and which files were read.

| Flag | Meaning |
|---|---|
| `--json` | Emit the raw result envelope instead of human text |
| `--output <file>` | Write output to a file rather than stdout |

### `stringlane check [project]`

The `inspect` report plus every validation finding, and an exit code.

| Flag | Meaning |
|---|---|
| `--json` | Emit the raw result envelope instead of human text |
| `--output <file>` | Write output to a file rather than stdout |
| `--fail-on <codes>` | Only these issue codes set exit 1 (comma-separated) |
| `--no-scan` | Skip the source-code scan for unused keys |
| `--ignore-key-pattern <glob>` | Do not report usage on keys matching this glob; repeatable |
| `--ignore-file <glob>` | Do not read source files matching this glob; repeatable |

`check` also runs the source-code scan described under [`scan`](#stringlane-scan-project). Its two findings are reported at **info** severity, so adopting a newer CLI does not turn a pipeline red and a recorded baseline does not flip. Opt in with `--fail-on unused_key`, or skip the scan entirely with `--no-scan`. Passing both is refused rather than accepted: with the scan off nothing can emit that code, so the gate could never fire.

If `.stringlane/baseline.yaml` exists it is read automatically, and only findings **not** in it decide the exit code. `--fail-on` filters what is left, so an accepted finding stays accepted either way. `--fail-on` can only soften a result, never harden it: a run that could not read your project still exits `2` whatever you pass.

### `stringlane plan [project]`

The exact list of what needs translating, for an agent to work from.

| Flag | Meaning |
|---|---|
| `--json` | Emit the raw result envelope instead of human text |
| `--output <file>` | Write output to a file rather than stdout |
| `--out <file>` | Alias for `--output` |
| `--target <locales>` | Only plan these locales (comma-separated) |

`--out` alone writes the human report. Pass `--json` as well to write the plan itself, which is the file `validate` and `apply` read. It refuses any path this project loaded as a translation file.

Exit `0` even when there is work to do, and `1` only when a file failed to parse.

### `stringlane validate [project]`

Checks an agent's translations without touching a file.

| Flag | Meaning |
|---|---|
| `--json` | Emit the raw result envelope instead of human text |
| `--plan <file>` | The plan file to work against (required) |
| `--candidates <file>` | The candidate translations to read (required) |

Exit `0` clean, `1` the candidates have problems, `2` could not run. A clean `validate` is not permission to write: see [How the write gate works](/docs/cli/review-and-recovery).

### `stringlane scan [project]`

Reads your source code and reports two things: keys the project defines that no call site names, and keys the code calls that the project never defined.

| Flag | Meaning |
|---|---|
| `--json` | Emit the raw result envelope instead of human text |
| `--output <file>` | Write output to a file rather than stdout |
| `--ignore-key-pattern <glob>` | Do not report on keys matching this glob; repeatable |
| `--ignore-file <glob>` | Do not read source files matching this glob; repeatable |

The text report caps both lists so its caveat stays on screen; `--json` carries them whole. Both flags add to `ignoreKeyPatterns` and `ignoreFilePatterns` in `stringlane.yaml` rather than replacing them.

"Not referenced" is what the scanner saw, not a verdict. See [Unused keys](/docs/cli/unused-keys) for what it cannot see and why it refuses rather than guessing.

## Writing commands

Each asks before writing, and refuses when there is no terminal to ask at unless `--yes` is passed.

### `stringlane init [project]`

Detects the project and writes `stringlane.yaml`.

| Flag | Meaning |
|---|---|
| `--yes` | Skip the confirmation prompt |
| `--force` | Overwrite an existing `stringlane.yaml` |

### `stringlane apply [project]`

Writes an agent's translations into your locale files.

| Flag | Meaning |
|---|---|
| `--plan <file>` | The plan file to work against (required) |
| `--candidates <file>` | The candidate translations to read (required) |
| `--json` | Emit the raw result envelope instead of human text |
| `--yes` | Skip the confirmation prompt |
| `--force-unlock` | Take the apply lock even if another process still holds it |

Re-runs every `validate` check, and additionally verifies under a per-project lock that no file it targets has changed since the plan was made. All or nothing. Exit `0` applied, `1` the candidates have problems, `2` could not run.

### `stringlane update [project]`

Translates the missing strings using your own AI provider, then writes them through the same gate.

| Flag | Meaning |
|---|---|
| `--provider <name>` | AI provider (default `openai`, or `$STRINGLANE_AI_PROVIDER`) |
| `--model <id>` | Model id (default: the provider's, or `$STRINGLANE_AI_MODEL`) |
| `--target <locales>` | Only translate these locales (comma-separated) |
| `--out-dir <dir>` | Also write the plan and candidates used, for auditing |
| `--json` | Emit the raw result envelope instead of human text |
| `--yes` | Skip the confirmation prompt |

The only command that spends money. Keys are read from the environment only: see [Translate with your own key](/docs/cli/translate-with-your-key).

### `stringlane describe [project]`

Writes key descriptions, so translations are made with context.

| Flag | Meaning |
|---|---|
| `--descriptions <file>` | JSON file of key and description entries to write |
| `--yes` | Skip the confirmation prompt |

### `stringlane add-locale [project]`

Creates the file for a locale this project does not have yet, in the right place and format.

| Flag | Meaning |
|---|---|
| `--locale <code>` | The locale code to create, e.g. `uk` or `fr-CA` (required) |
| `--json` | Emit the raw result envelope instead of human text |
| `--yes` | Skip the confirmation prompt |

The file it creates is empty apart from the locale marker, on purpose: every key then shows up as missing in the next plan, rather than looking like translations you chose to leave empty. In a project with more than one origin it creates one file per origin, in a single atomic write: either the locale exists everywhere it should or it exists nowhere. It does not make your app *offer* the language.

### `stringlane add-key [project]`

Adds a new key and its source string to the base locale.

| Flag | Meaning |
|---|---|
| `--key <key>` | The key to create, e.g. `checkout.title` (required) |
| `--value <string>` | What the key says in your base locale (required) |
| `--json` | Emit the raw result envelope instead of human text |
| `--yes` | Skip the confirmation prompt |

Base locale only: every other locale then reports the key as missing in the next plan, and the ordinary plan, validate and apply chain fills it. The confirmation prompt names the key and the string. Both flags are required, because an empty value is what an untranslated cell looks like. A key name the format cannot carry is refused before it breaks a build.

### `stringlane context [project]`

Shows, or sets, the product, branding, locale and terminology context stored in `stringlane.yaml`.

| Flag | Meaning |
|---|---|
| `--from <file>` | JSON file of context fields to write; omit to show what is set |
| `--yes` | Skip the confirmation prompt |

### `stringlane baseline write [project]`

Records today's issues to `.stringlane/baseline.yaml`, so CI can start clean.

| Flag | Meaning |
|---|---|
| `--yes` | Skip the confirmation prompt |

## Agent and setup commands

### `stringlane mcp [project]`

Serves the project to an AI agent over MCP on stdio. A server, not a command: it stays running until the agent disconnects. It has no flags of its own.

### The `--host` values

Four commands take `--host`, and all four accept the same eight names:

`claude-code` · `codex` · `vscode` · `gemini-cli` · `cursor` · `windsurf` · `zed` · `opencode`

Every one of them **prints** a real configuration for the exact file that host reads. What differs is whether `--apply` then writes it. StringLane will not merge into another program's config file until somebody has run that program and recorded what it actually writes, and today that is `claude-code`. On the other seven, `--apply` prints and exits non-zero: the entry is still there to copy, and the exit code is the command telling you it changed nothing.

Two exceptions, both deliberate. `setup rules` applies everywhere, because it writes `AGENTS.md` — your file, in your repository. `--remove` works everywhere too: refusing to install is cautious, and refusing to uninstall is a trap. See [Agent hosts](/docs/cli/agent-hosts).

### `stringlane setup mcp`

Shows, or with `--apply` writes, your agent's MCP configuration.

| Flag | Meaning |
|---|---|
| `--host <name>` | Which agent host; see above |
| `--scope <scope>` | Which config to write: `user` (default) or `project` |
| `--apply` | Write the change instead of printing it |
| `--force` | Replace an existing StringLane entry |

Without `--apply` it prints the entry and the exact file, and writes nothing. The entry is given in that host's own shape: these hosts want it under four different container keys, and one under the wrong key is not an error on any of them — the host starts, lists no StringLane tools, and says nothing about why. With `--apply` on Claude Code it backs the file up first and tells you where the backup went.

### `stringlane setup hooks`

Shows, or with `--apply` writes, the write guard in your agent's settings.

| Flag | Meaning |
|---|---|
| `--host <name>` | Which agent host; see above |
| `--scope <scope>` | `user` (default) or `project` |
| `--apply` | Write the change instead of printing it |
| `--remove` | Take StringLane's entries back out, leaving every other one |

Not every host has one. Windsurf's pre-write hook is exit-code only, with no message back to the model, so StringLane ships no dialect for it. VS Code's hook installs but guards nothing yet, and the command prints that warning under the document.

### `stringlane setup rules`

Shows, or with `--apply` writes, three localization rules into your `AGENTS.md`, between two HTML-comment markers.

| Flag | Meaning |
|---|---|
| `--host <name>` | Adds that host's own note about how it reads `AGENTS.md`. Changes nothing that is written |
| `--apply` | Write the change instead of printing it |
| `--remove` | Take the marked block back out |

Everything outside the markers is left byte for byte as it was, and running it twice still leaves one block. It refuses rather than guessing when the markers are ambiguous, and names the line numbers so you can see what it saw. This is the one setup command that writes on every host.

### `stringlane setup guard`

Prints the permission block that makes Zed or OpenCode confirm before an agent edits a locale file, in that host's own pattern language — Zed's patterns are regular expressions, OpenCode's are globs.

| Flag | Meaning |
|---|---|
| `--host <name>` | `zed` or `opencode` |

Print only: both files are JSONC, and a JSON writer would discard your comments on the way back out. It is a **snapshot** of the files loaded when you ran it, so a locale file added later is not covered until you run it again, and the block says so in its own first line. Exit `0` with the block on stdout; `2` if you passed `--apply`, or if the folder is not a project or has no locale files.

### `stringlane hook`

Answers an agent host's pre-write hook. Reads stdin; not run by hand. It answers for every host that has a dialect, recorded or not — a printed hook document pointing at a command that refuses it would be a dead end rather than an escape hatch.

### `stringlane upgrade`

Asks the npm registry what the latest version is and prints the command to install it. **It never installs anything itself.**

Most commands also check quietly, at most once a day, printing a one-off notice on stderr when you are behind. `hook` never does, because it runs on every file an agent touches, and `mcp` checks when it starts rather than when it ends. That background check is off wherever telemetry is: `STRINGLANE_NO_UPDATE_CHECK=1`, `DO_NOT_TRACK=1`, `STRINGLANE_TELEMETRY=0` or `CI=true`. This command ignores all four, because you typed it.

Exit `0` whether you are current or behind, and `3` when the registry gave no answer — not `2`, which would say you invoked it wrong.

### `stringlane telemetry status | on | off`

Reports or changes whether anonymous usage data is sent, and names any environment variable currently overriding the stored setting. Writes only to StringLane's own state directory, never to your project.

## Global flags

| Flag | Meaning |
|---|---|
| `--help` | Show help. Works per command |
| `--version` | Print the version |

## Environment variables

| Variable | Effect |
|---|---|
| `STRINGLANE_STATE_HOME` | Absolute path for StringLane's own state. A relative value is ignored |
| `STRINGLANE_TELEMETRY=0` | Disable usage reporting |
| `DO_NOT_TRACK=1` | Disable usage reporting |
| `CI=true` | Disable usage reporting and the background update check. Set by most CI providers already |
| `STRINGLANE_NO_UPDATE_CHECK=1` | Disable the background update check only, leaving telemetry as it is |
| `STRINGLANE_AI_PROVIDER` | Default provider for `update` |
| `STRINGLANE_AI_MODEL` | Default model for `update` |
| `STRINGLANE_AI_BASE_URL` | Base URL for a local, OpenAI-compatible runner |
| `STRINGLANE__API_KEY` | Provider key, preferred over the conventional name |

No environment variable can turn telemetry **on** over a stored opt-out.
