---
title: CLI Reference
description: Complete CLI reference for @envlock/cli – validate, inspect, and generate environment contracts
url: https://pr-1-8289d63b6330.thally.app/envlock/cli
---

# CLI Reference

Complete CLI reference for @envlock/cli – validate, inspect, and generate environment contracts

The `@envlock/cli` package provides the `envlock` command for validating environment variables, generating example files, and inspecting schemas locally or in CI pipelines.

## Installation

Install as a dev dependency:

```sh
npm install -D @envlock/cli
```

The binary is available as `envlock` in your `npm scripts` or `npx envlock` from the command line.

## Configuration

The CLI looks for a schema in `envlock.config.mjs` or `envlock.config.js` in your project root:

```ts
// envlock.config.mjs
import { defineEnv, env } from "@envlock/core";

export default defineEnv({
  NODE_ENV: env.enum(["development", "test", "production"]).default("development"),
  PORT: env.port().default(3000).describe("HTTP listen port"),
  DATABASE_URL: env.url({ protocols: ["postgres:"] }).secret(),
  LOG_LEVEL: env.enum(["debug", "info", "warn", "error"]).default("info"),
});
```

Export the schema as the default export or as a named `schema` export. You can also pass `--schema <path>` to any command to override the default location.

## Commands

### `envlock check`

Validates environment variables against your contract.

```sh
envlock check [--schema <path>] [--env-file <path>] [--merge-process-env] [--strict] [--json]
```

**Flags:**

| Flag | Description |
|---|---|
| `--schema <path>` | Path to schema file (default: `envlock.config.mjs` or `envlock.config.js`) |
| `--env-file <path>` | Path to `.env` file to validate (default: `process.env`) |
| `--merge-process-env` | Merge env file over process.env; file wins on conflicts |
| `--strict` | Report undeclared variables in env file (ignored for process.env) |
| `--json` | Output JSON: `{ ok, issues, source }` |

**Output (text mode):**

Success:
```
ok: process.env satisfies 4 declared variable(s)
```

Failure (table format):
```
error: 2 issue(s) in .env

KEY              CODE     MESSAGE
PORT             invalid  expected a finite number
DATABASE_URL     missing  required variable is not set
```

**Output (JSON mode):**

```json
{
  "ok": false,
  "source": "process.env",
  "issues": [
    {
      "key": "PORT",
      "code": "invalid",
      "message": "expected a finite number",
      "received": "abc"
    },
    {
      "key": "DATABASE_URL",
      "code": "missing",
      "message": "required variable is not set"
    }
  ]
}
```

**Exit code:** 0 on success, 1 on validation failure.

---

### `envlock example`

Generates a `.env.example` file from your schema.

```sh
envlock example [--schema <path>] [--out <path>] [--check]
```

**Flags:**

| Flag | Description |
|---|---|
| `--schema <path>` | Path to schema file (default: `envlock.config.mjs` or `envlock.config.js`) |
| `--out <path>` | Write to file instead of stdout (default: `.env.example`) |
| `--check` | Compare rendered file to `--out` or `.env.example`; exit 1 if out of date |

**Without `--out` or `--check`:** Outputs the rendered example to stdout.

**With `--out <path>`:** Writes the file and shows:
```
ok: wrote .env.example (4 variable(s))
```

**With `--check`:** Compares the current file to what it should be.

If up to date:
```
ok: .env.example is up to date
```

If out of date:
```
error: .env.example is out of date; run `envlock example --out .env.example`
```

If missing:
```
error: .env.example does not exist; run `envlock example --out .env.example`
```

**Exit code:** 0 on success (including `--out`, which always overwrites). With `--check`, exit 1 if the file is out of date or missing.

---

### `envlock diff`

Compares a `.env` file against your contract and reports differences.

```sh
envlock diff [--schema <path>] [--env-file <path>] [--json]
```

**Flags:**

| Flag | Description |
|---|---|
| `--schema <path>` | Path to schema file (default: `envlock.config.mjs` or `envlock.config.js`) |
| `--env-file <path>` | Path to `.env` file (default: `.env`) |
| `--json` | Output JSON with categorized issues |

The `diff` command always validates with `--strict`, reporting undeclared variables.

**Output (text mode):**

```
Missing (1):
  DATABASE_URL

Unknown (1):
  EXTRA_VAR

Invalid (1):
  PORT: expected a finite number (received "abc")
```

**Output (JSON mode):**

```json
{
  "ok": false,
  "source": ".env",
  "missing": ["DATABASE_URL"],
  "unknown": ["EXTRA_VAR"],
  "invalid": [
    {
      "key": "PORT",
      "code": "invalid",
      "message": "expected a finite number",
      "received": "abc"
    }
  ]
}
```

**Exit code:** 0 when clean, 1 otherwise.

---

### `envlock inspect`

Describes all variables declared in your schema with their types, constraints, defaults, and descriptions.

```sh
envlock inspect [--schema <path>] [--json]
```

**Flags:**

| Flag | Description |
|---|---|
| `--schema <path>` | Path to schema file (default: `envlock.config.mjs` or `envlock.config.js`) |
| `--json` | Output JSON array of variable metadata |

**Output (text mode, table format):**

```
KEY              TYPE      REQUIRED  DEFAULT        SECRET  DESCRIPTION
NODE_ENV         enum      yes       development    no
PORT             port      yes       3000           no      HTTP listen port
DATABASE_URL     url       yes       -              yes     Connection string
LOG_LEVEL        enum      yes       info           no
```

**Output (JSON mode):**

```json
[
  {
    "key": "NODE_ENV",
    "type": "enum",
    "required": true,
    "hasDefault": true,
    "default": "development",
    "secret": false,
    "description": null,
    "example": null,
    "constraints": "one of: development, test, production"
  },
  {
    "key": "PORT",
    "type": "port",
    "required": true,
    "hasDefault": true,
    "default": 3000,
    "secret": false,
    "description": "HTTP listen port",
    "example": null,
    "constraints": "integer 1-65535"
  }
]
```

**Exit code:** 0 always (inspection is read-only).

---

### `envlock init`

Creates a starter `envlock.config.mjs` file with common variables.

```sh
envlock init
```

This command writes a new `envlock.config.mjs` in the current directory with:
- `NODE_ENV` (enum: development, test, production; default: "development")
- `PORT` (port; default: 3000)
- `DATABASE_URL` (URL, secret)
- `LOG_LEVEL` (enum: debug, info, warn, error; default: "info")

**Output (success):**
```
ok: wrote envlock.config.mjs; edit it, then run `envlock check`
```

**Output (file already exists):**
```
error: envlock.config.mjs already exists; delete it first if you want a fresh starter
```

The command refuses to overwrite existing config. Delete the file if you want to regenerate it.

**Exit code:** 0 on success, 1 if any config candidate exists.

---

### `envlock --help`

Displays global help with all commands and flags.

```sh
envlock --help
```

Output includes the version number and usage for all commands.

---

### `envlock --version`

Shows the CLI version.

```sh
envlock --version
```

Output: `0.1.0`

---

## Exit Codes

| Code | Constant | Meaning |
|---|---|---|
| 0 | `ok` | Validation passed, no drift, or operation completed successfully |
| 1 | `failure` | Validation failed, drift detected, or init found existing config |
| 2 | `usage` | Unknown command, unknown flag, missing flag value, or unexpected argument |
| 3 | `config` | Config not found, schema import failed, schema file unreadable, or env file unreadable |

## Error Messages

### Usage Errors (exit 2)

These errors indicate incorrect command-line syntax:

| Error | Cause |
|---|---|
| `error: unknown command "<name>"` | Command not recognized |
| `error: unknown flag --<name>` | Flag not recognized for this command |
| `error: flag --<name> requires a value` | Flag expects a value but none provided |
| `error: flag --<name> does not take a value` | Flag does not accept a value |
| `error: unexpected argument "<arg>"` | Positional argument where not expected |

### Config Errors (exit 3)

These errors indicate schema or file loading problems:

| Error | Cause |
|---|---|
| `error: no envlock.config.mjs or envlock.config.js found in <cwd>` | Schema not found; use `--schema` or run `envlock init` |
| `error: schema file not found: <path>` | Specified schema file does not exist |
| `error: failed to import <path>: <detail>` | Schema file exists but failed to import (syntax error, missing module, etc.) |
| `error: <path> must export default defineEnv({...}) (or export a named schema)` | Schema file does not export a valid schema |
| `error: could not read env file <path>: <detail>` | Env file is not readable |

---

## Programmatic Usage

Use `runCli` to integrate the CLI into your own tools or scripts.

```ts
import { runCli, EXIT_CODES, type CliIo } from "@envlock/cli";

const io: CliIo = {
  stdout: (chunk) => process.stdout.write(chunk),
  stderr: (chunk) => process.stderr.write(chunk),
  cwd: process.cwd(),
  env: process.env,
};

// Run envlock check
const code = await runCli(["check", "--env-file", ".env"], io);

if (code === EXIT_CODES.ok) {
  console.log("Validation passed");
} else if (code === EXIT_CODES.failure) {
  console.error("Validation failed");
} else if (code === EXIT_CODES.usage) {
  console.error("Usage error");
} else if (code === EXIT_CODES.config) {
  console.error("Config error");
}
```

**`CliIo` interface:**

```ts
interface CliIo {
  readonly stdout: (chunk: string) => void;  // Write to stdout
  readonly stderr: (chunk: string) => void;  // Write to stderr
  readonly cwd: string;                       // Working directory for file resolution
  readonly env: Readonly<Record<string, string | undefined>>;  // Environment variables
}
```

**`ExitCode` type:**

```ts
type ExitCode = 0 | 1 | 2 | 3;
```

**`EXIT_CODES` constants:**

```ts
const EXIT_CODES = {
  ok: 0,        // Success
  failure: 1,   // Validation or operation failed
  usage: 2,     // Command-line usage error
  config: 3,    // Configuration or file loading error
};
```