Skip to content

Latest commit

 

History

History
452 lines (307 loc) · 23.3 KB

File metadata and controls

452 lines (307 loc) · 23.3 KB


logo




Localess CLI

The @localess/cli package is the official command-line interface for the Localess headless CMS platform. It provides commands to authenticate with your Localess instance, synchronize translations, generate TypeScript type definitions, and sync code-defined schemas with your space.

Requirements

  • Node.js >= 24.0.0

Installation

# Install as a project dev dependency (recommended)
npm install @localess/cli -D

# Or install globally
npm install @localess/cli -g

Features

  • 🔐 Authentication — Secure credential storage for CLI and CI/CD environments
  • 🌐 Translations — Push, pull, and diff translation files against your Localess space
  • 🛡️ Type Generation — Generate TypeScript type definitions from your Localess content schemas for end-to-end type safety
  • 🧬 Schema Sync — Define schemas in code and pull, diff, and push them against your Localess space

Authentication

localess login

Authenticate with your Localess instance. Credentials are validated immediately and stored securely in .localess/credentials.json with restricted file permissions (0600).

localess login --origin <origin> --space <space_id> --token <api_token>

If any option is omitted, the CLI will interactively prompt for the missing values. Credentials are validated with a GET /spaces/{spaceId} call before being saved, and .localess is appended to .gitignore automatically (the file is created if absent; the entry is skipped if already present). If a session already exists (env vars or file), login prints Already logged in. and exits without prompting — run localess logout first to switch credentials.

Options:

Flag Description
-o, --origin <origin> Localess instance URL (e.g., https://my-localess.web.app)
-s, --space <space> Space ID (found in Localess Space settings)
-t, --token <token> API token (input is masked for security)
-v, --verbose Print verbose debug output

Examples:

# Interactive login (prompts for any missing values)
localess login

# Non-interactive login (CI/CD)
localess login --origin https://my-localess.web.app --space MY_SPACE_ID --token MY_API_TOKEN

Authentication via Environment Variables

For CI/CD pipelines, you can provide credentials through environment variables instead of running localess login. The CLI automatically reads these variables and skips the file-based credentials. All three must be set — if any is missing, the CLI falls back to .localess/credentials.json in the current working directory:

export LOCALESS_ORIGIN=https://my-localess.web.app
export LOCALESS_SPACE=MY_SPACE_ID
export LOCALESS_TOKEN=MY_API_TOKEN

localess translation pull en --path ./public/locales/en.json
Variable Description
LOCALESS_ORIGIN Localess instance URL
LOCALESS_SPACE Space ID
LOCALESS_TOKEN API token

localess logout

Clear stored credentials from .localess/credentials.json (the file is overwritten with {}).

localess logout

If you authenticated via environment variables, those must be unset manually — logout only affects file-based credentials.


Translations Management

localess translation was previously localess translations, and localess type was localess types. The old plural names still work as aliases for backward compatibility.

localess translation push <locale>

Upload a local JSON translation file to Localess. Prints only the keys the selected --type acts on, as +/~/- lines under a summary such as Added 1 translation in locale "en". or, with --dry-run, Dry run: would add 1 translation in locale "en": (No translations to add for locale "en". when there is nothing to do). It does not print a full diff; use translation diff for that. update-existing, delete-missing-key and delete-missing-value first ask the server for a dry run, list the affected keys and prompt for confirmation (skippable with -y, --yes, automatically skipped under --dry-run, and nothing is pushed when the dry run reports no keys); add-missing never prompts since it is additive-only. Declining the prompt prints Aborted. and exits 1.

The server responds with { message, ids, dryRun? }, where ids lists only the keys the push type wrote (or would write).

localess translation push <locale> --path <file> [options]

Arguments:

Argument Description
<locale> ISO 639-1 locale code (e.g., en, de, fr)

Options:

Flag Default Description
-p, --path <path> (required) Path to the JSON translations file
-f, --format <format> flat File format: flat or nested
-t, --type <type> add-missing Update strategy: add-missing, update-existing, delete-missing-key, or delete-missing-value
--dry-run false Preview changes without applying them (also skips the confirmation prompt)
-y, --yes false Skip the confirmation prompt for update-existing/delete-missing-key/delete-missing-value
-v, --verbose false Print verbose debug output

Update Strategies:

Type Description Confirmation
add-missing Adds translations for keys that do not yet exist in Localess Never — safe for unattended CI
update-existing Updates translations for keys that already exist in Localess — overwrites any edits made in Localess since your last pull Prompted (unless -y/--dry-run, or nothing differs)
delete-missing-key Deletes translations for keys absent from the local file in every locale — run it with a complete file (normally the source locale) Prompted (unless -y/--dry-run, or nothing is stale)
delete-missing-value Removes only <locale>'s value of keys absent from the local file; other locales keep theirs Prompted (unless -y/--dry-run, or nothing is stale)

File Formats:

  • flat — A flat JSON object where keys may use dot notation:

    {
      "common.submit": "Submit",
      "nav.home": "Home"
    }
  • nested — A nested JSON object that is automatically flattened before uploading:

    {
      "common": { "submit": "Submit" },
      "nav": { "home": "Home" }
    }

Examples:

# Push English translations (add missing keys only)
localess translation push en --path ./locales/en.json

# Push with update-existing strategy (prompts for confirmation)
localess translation push en --path ./locales/en.json --type update-existing

# Same, but skip the confirmation prompt (e.g. scripted/CI use)
localess translation push en --path ./locales/en.json --type update-existing --yes

# Delete keys absent from the source file, in every locale (prompts for confirmation)
localess translation push en --path ./locales/en.json --type delete-missing-key

# Remove only the German values of keys absent from de.json (prompts for confirmation)
localess translation push de --path ./locales/de.json --type delete-missing-value

# Preview changes without applying (dry run)
localess translation push en --path ./locales/en.json --dry-run

# Push nested-format translations
localess translation push de --path ./locales/de.json --format nested

localess translation pull <locale>

Pull translations from your Localess space and save them to a local file. Keys are sorted alphabetically (recursively for nested) so the output is stable across runs and diff-friendly in git. In nested, a key that is also the parent of other keys (button beside button.save) keeps its child keys; its own value is left out and listed in a warning. Use flat to keep every key.

localess translation pull <locale> --path <file> [options]

Arguments:

Argument Description
<locale> ISO 639-1 locale code (e.g., en, de, fr)

Options:

Flag Default Description
-p, --path <path> (required) Output file path
-f, --format <format> flat File format: flat or nested
--draft false Pull the draft (unpublished) version
--raw false Pull only the values stored for the locale, without fallback filling — for files you edit and push back. Cannot be combined with --draft
-v, --verbose false Print verbose debug output

Examples:

# Pull English translations as flat JSON
localess translation pull en --path ./locales/en.json

# Pull German translations as nested JSON
localess translation pull de --path ./locales/de.json --format nested

# Pull draft (unpublished) translations
localess translation pull en --path ./locales/en.json --draft

localess translation diff <locale>

Read-only comparison between a local translations file and your Localess space — published by default, the draft with --draft, or the values stored for the locale with --raw (exactly what push acts on). Groups keys into Only in file/Different/Only in Localess sections (color-coded, git-diff style +/~/- symbols); unchanged keys collapse into a single count by default so drift stays visible even with thousands of translations. Exits 1 if anything differs (useful as a CI drift gate), 0 when everything is unchanged. Does not modify anything — use push to apply changes.

localess translation diff <locale> --path <file> [options]

Arguments:

Argument Description
<locale> ISO 639-1 locale code (e.g., en, de, fr)

Options:

Flag Default Description
-p, --path <path> (required) Path to the local translations file
-f, --format <format> flat File format: flat or nested
--draft false Compare against the draft version
--raw false Compare against stored values, without fallback filling — what push acts on. Cannot be combined with --draft
-a, --all false Also print unchanged keys
-v, --verbose false Print verbose debug output

Examples:

# Compare a local file against the space (exits 1 on drift)
localess translation diff en --path ./locales/en.json

# Also list unchanged keys
localess translation diff en --path ./locales/en.json --all

# Compare against stored values — what push would act on
localess translation diff de --path ./locales/de.json --raw

# CI drift gate
- run: localess translation diff en --path ./locales/en.json

A locale the space doesn't have is refused (checked when the token can read the space).

Syncing translations

The translation commands are stateless building blocks: each has a fixed direction, and you choose the direction per locale and per step.

Source (pull, diff) Flag
published, fallback-filled (default)
draft, fallback-filled --draft
stored values, no fallback filling --raw
  • pull replaces the file with Localess's version — Localess wins.
  • push --type … applies one operation from the file — the file wins: add-missing, update-existing, delete-missing-key (every locale), delete-missing-value (this locale only).
  • diff shows Only in file / Different / Only in Localess against the same source pull would use and exits 1 on drift; diff --raw predicts exactly what push would act on.

Typical setups:

  • Code owns everything: push --type add-missing and update-existing per locale; diff --raw as a CI gate.
  • Localess owns everything: pull per locale at build time; translators edit in Localess.
  • Code owns keys and the source language, translators own the rest: push en --type add-missing / update-existing / delete-missing-key from the source file; pull <locale> for the other locales.

TypeScript Type Generation

localess type generate

Fetch your space's schema definitions from Localess and generate TypeScript type definitions. The output file provides full type safety when working with Localess content in your TypeScript projects: one interface per ROOT/NODE schema (with _id and a literal _schema discriminator), a string-literal union per ENUM schema, the ContentAsset/ContentLink/ContentReference/ContentRichText helper types, and a ContentData union of all ROOT schemas.

localess type generate [--path <output_path>] [--prefix <prefix>]

Options:

Flag Default Description
-p, --path <path> .localess/localess.d.ts Path to write the generated TypeScript definitions file
--prefix <prefix> '' Prefix to prepend to all generated type names
-v, --verbose false Print verbose debug output

Note: Your API token must have Development Tools permission enabled in Localess Space settings.

Example:

# Generate types to the default location
localess type generate

# Generate types to a custom path
localess type generate --path src/types/localess.d.ts

# Prefix all generated type names (e.g. PascalCase namespacing to avoid collisions)
localess type generate --prefix Localess
# produces `LocalessPage`, `LocalessHeroBlock`, `LocalessContentAsset`, etc.

Using generated types:

import type { Page, HeroBlock } from './.localess/localess';
import { getLocalessClient } from "@localess/react";

const client = getLocalessClient();
const content = await client.getContentBySlug<Page>('home', { locale: 'en' });
// content.data is now fully typed as Page

Schema Commands

Define Localess schemas in TypeScript with @localess/schema (defineSchema/defineEnum/defineConfig) and sync them bidirectionally with your Localess space. Entry-file convention: a TS/JS file exporting the result of defineConfig() — by convention schemas/index.ts, but any path works.

Prerequisite: Your API token must have the Development Tools permission (DEV_TOOLS) — same as type generate. No dedicated schema permission exists.

localess schema validate <entry>

Offline — no login, no network call. Runs @localess/schema's validate() against the entry's config and prints each issue (ERROR/WARNING, code, path, message). Exits 1 when any error-severity issue is present; 0 otherwise (warnings alone don't fail).

localess schema validate ./schemas/index.ts
localess schema validate ./schemas/index.ts --format json   # machine-readable, for CI

localess schema pull [--path <dir>] [--print-width <n>]

Fetches the space's schemas and (re)generates one TypeScript definition file per schema (kebab-case file name, e.g. HeroBlock → hero-block.ts) plus index.ts (a defineConfig() call) into --path (default schemas). Repeatable and safe to re-run — only ever overwrites/deletes files it previously generated (marked with a header comment); a same-named hand-written file without that marker is skipped and reported, and never overwritten.

localess schema pull
localess schema pull --path src/schemas
localess schema pull --print-width 100   # match your own .prettierrc printWidth
  • Every field is emitted wrapped in defineField(...) (imported from @localess/schema alongside defineSchema), not as a bare object literal, so hand-edits to a pulled file still get defineField's excess-property checking. Schemas with no fields import only defineSchema; ENUM schemas use defineEnum.
  • Cross-schema references (OPTION/OPTIONS source, SCHEMA/SCHEMAS schemas) that point at another pulled schema become import { X } from './x' statements and by-value refs; ids not present in the pulled set stay plain strings.
  • A defineField(...) call wraps to one property per line once it would exceed --print-width columns (default 80, Prettier's default) — set it to match your project's printWidth.
  • Output is deterministic — the same server state (and --print-width) produces byte-identical files.

localess schema diff <entry>

Read-only comparison between the entry's code-defined schemas and the space — same grouped/colored report layout as translation diff, with schema-specific Create/Update/Stale sections (unchanged schemas collapsed into a count by default). Exits 1 if anything differs (CI drift gate), 0 when everything is unchanged.

localess schema diff ./schemas/index.ts
localess schema diff ./schemas/index.ts --all   # also list unchanged schemas

localess schema push <entry> [--dry-run] [--delete] [-a|--all] [-y|--yes]

Validates, diffs, then pushes the entry's schemas to the space. The pre-push diff uses the same grouped/colored report as schema diff (-a, --all to also list unchanged schemas).

localess schema push ./schemas/index.ts --dry-run   # preview only
localess schema push ./schemas/index.ts             # upsert: create/update, never delete
localess schema push ./schemas/index.ts --delete    # sync: also delete schemas absent from code
localess schema push ./schemas/index.ts --delete -y # sync, skip the deletion confirmation prompt
  • Aborts (exit 1) without pushing if validate() reports any error (warnings are printed but don't block).
  • Default mode is upsert — creates and updates, never deletes. Schemas on the server but absent from code are reported as stale and left alone.
  • --delete switches to sync mode, which also deletes stale schemas. Without --yes, you're asked to confirm the exact list before anything is deleted; --dry-run skips the prompt (nothing is written either way), and no prompt is shown when nothing is stale.
  • Prints the server's final counts: created, updated, deleted, unchanged (prefixed with [DryRun] under --dry-run).
  • Reconciles the pre-push diff against the server's returned ids and prints a ⚠ Prediction mismatch warning (without failing) for any schema whose predicted status differs from the server's actual outcome — e.g. a concurrent change made between the preview and the push. stale entries are only checked in sync mode, since upsert never sends them.

Stored Files

File Description
.localess/credentials.json Stored login credentials (created by localess login, mode 0600)
.localess/localess.d.ts Generated TypeScript definitions (created by localess type generate)
schemas/*.ts, schemas/index.ts Generated schema definitions (created by localess schema pull; path via --path)

localess login appends .localess to .gitignore automatically. If you want to commit generated types, refine that entry to .localess/credentials.json afterwards.


Platform Compatibility Check

The CLI and the Localess platform are released in lockstep. Before each command that talks to the platform, the CLI reads <origin>/assets/version.json and blocks the command (exit 1, before any API call) if the platform's major version differs from the CLI's or the platform is below the command's minimum (currently 4.1.0 for every platform-facing command). login, logout, and schema validate are exempt. The check is skipped silently when no credentials are configured or the version can't be determined (non-2xx, unreachable, malformed, or a 3-second timeout). Set LOCALESS_SKIP_VERSION_CHECK to any value to bypass it.

Update Notifications

Every command checks the npm registry for a newer @localess/cli version (3-second timeout, failures ignored silently) and, after the command finishes, prints an "Update available" box with the npm install --save-dev @localess/cli@<tag> command when one exists. Pre-release (-dev.*) installs also check the dev dist-tag; a newer stable release always wins over a newer dev build.

API Errors

Any non-OK response from the Localess API is rendered as a boxed error (Status, optional Code, redacted URL, and a Hint) and the command exits 1. 401 and 403 hints link directly to your space's token settings page and, for 403, list the permission(s) the token is missing. Network errors and 5xx responses are retried 3 times with a 500 ms delay before failing.


AI Coding Agents

This package ships a SKILL.md file that provides AI coding agents (GitHub Copilot, Claude Code, Cursor, and others) with accurate, up-to-date APIs, patterns, and best practices. Most agents automatically read SKILL.md when starting a session.

Using SKILL.md in your project

SKILL.md is included in the npm package, so it is available locally after installation. Reference it from your project's AGENTS.md to ensure your agent reads accurate Localess documentation every session:

## Localess

@node_modules/@localess/cli/SKILL.md

The @ prefix is the syntax used by most agent tools (GitHub Copilot, Claude Code, Cursor) to import file contents inline into the agent context.

When you change the public API of this package, update SKILL.md alongside your code:

  • New option or parameter → add it to the relevant options table and usage example
  • Changed behaviour → update the description and any affected code snippets
  • Deprecated API → mark it clearly and point to the replacement
  • New command or subcommand → add a full entry with all flags and examples

License

MIT