diff --git a/src/pages/docs/features/_meta.ts b/src/pages/docs/features/_meta.ts
index a6d00f9..c2bb629 100644
--- a/src/pages/docs/features/_meta.ts
+++ b/src/pages/docs/features/_meta.ts
@@ -56,5 +56,8 @@ export default {
},
"mcp-integration": {
"title": "MCP Integration"
+ },
+ "cli": {
+ "title": "CLI (lfc)"
}
-};
+};
\ No newline at end of file
diff --git a/src/pages/docs/features/cli.mdx b/src/pages/docs/features/cli.mdx
new file mode 100644
index 0000000..07213cd
--- /dev/null
+++ b/src/pages/docs/features/cli.mdx
@@ -0,0 +1,250 @@
+---
+title: CLI (lfc)
+tags:
+ - cli
+ - lfc
+ - operators
+ - tooling
+ - automation
+---
+
+import { Callout } from "nextra/components";
+
+`lfc` is the command-line interface for Lifecycle. It lets you view and manage
+preview environments (builds), redeploy services, stream logs, host static
+sites, and validate configuration — straight from your terminal. It is built
+for **humans and agents**: every command supports `--json`, human-readable
+messages go to `stderr`, and machine output goes to `stdout`.
+
+
+ The CLI ships with **no deployment URLs baked in**. You point it at your own
+ Lifecycle deployment once with `lfc init`. All examples below use generic
+ placeholder hosts such as `https://lifecycle.example.com` and `myorg/myrepo` —
+ substitute your own values.
+
+
+## Overview
+
+Requires Node ≥ 20.
+
+```sh
+npm install -g lfc-cli
+lfc --help
+```
+
+- **`--help` everywhere.** `lfc --help` works on every command and
+ subcommand and lists all flags.
+- **`--json` everywhere.** Add `--json` for machine-readable output, or set
+ `LFC_JSON=1` to enable it globally.
+- **Humans and agents.** Interactive prompts appear only on a TTY; without one,
+ commands fail fast and name the missing flag/argument, so scripts and agents
+ stay deterministic. Run `lfc llms` to print agent-oriented instructions.
+
+## How it works
+
+`lfc` is a thin client over your Lifecycle deployment's HTTP API. Nothing about
+your deployment is hard-coded — you configure it once and the CLI stores a
+**profile** locally.
+
+- **Profiles.** Each profile points at one deployment (API URL, optional UI URL,
+ and auth settings). You can keep several (e.g. `default`, `staging`, `local`)
+ and switch between them. Config lives at
+ `~/.config/lifecycle-cli/config.json`.
+- **Authentication.** Deployments that enforce SSO use Keycloak (Authorization
+ Code + PKCE, or device code for headless machines). Tokens are cached per
+ profile under `~/.config/lifecycle-cli/tokens/` and refresh automatically.
+ Auth-less deployments (`authEnabled: false`) skip all of this.
+- **Output & scripting.** JSON goes to `stdout`; informational messages go to
+ `stderr`. Commands return meaningful exit codes:
+
+ | Code | Meaning |
+ | ---- | ----------------------------------------------------------- |
+ | `0` | success |
+ | `1` | failure (API error, failed build, invalid schema) |
+ | `2` | watch timeout (`--watch` did not converge in time) |
+ | `3` | not found (unknown uuid, no matching build, no config file) |
+ | `4` | authentication error (not logged in, expired session) |
+
+
+ On exit code `4`, run `lfc login` (or `lfc login --device` on a headless
+ machine) and retry. Verify who you are with `lfc whoami`.
+
+
+## Configuration
+
+Point the CLI at your deployment. Interactively:
+
+```sh
+lfc init
+```
+
+Or non-interactively (for scripts, dotfiles, onboarding docs):
+
+```sh
+# SSO deployment
+lfc init \
+ --api-url https://lifecycle.example.com \
+ --ui-url https://lifecycle-ui.example.com \
+ --issuer https://sso.example.com/realms/lifecycle
+
+# auth-less deployment (e.g. local)
+lfc init --api-url http://localhost:8080 --no-auth --name local
+```
+
+`init` verifies the URL is reachable, writes the profile, and (when auth is on)
+starts the SSO login. Useful flags: `--ui-url` (enables `lfc builds open`),
+`--no-login` (skip the login step), `--device` (device-code login),
+`--name `, `--force` (overwrite an existing profile).
+
+### Config file structure
+
+Configuration lives at `~/.config/lifecycle-cli/config.json` and holds one entry
+per deployment under `profiles`, with the active one named by `currentProfile`:
+
+```json
+{
+ "currentProfile": "default",
+ "profiles": {
+ "default": {
+ "apiUrl": "https://lifecycle.example.com",
+ "uiUrl": "https://lifecycle-ui.example.com",
+ "authEnabled": true,
+ "keycloak": {
+ "issuer": "https://sso.example.com/realms/lifecycle",
+ "clientId": "lifecycle-cli"
+ }
+ },
+ "local": {
+ "apiUrl": "http://localhost:8080",
+ "authEnabled": false
+ }
+ }
+}
+```
+
+| Field | Description |
+| ------------------- | ------------------------------------------------------------------- |
+| `apiUrl` | Base URL of the Lifecycle API (required). |
+| `uiUrl` | Base URL of the Lifecycle UI (optional; enables `lfc builds open`). |
+| `authEnabled` | Whether the deployment enforces SSO. |
+| `keycloak.issuer` | Keycloak realm issuer URL (when `authEnabled` is `true`). |
+| `keycloak.clientId` | OAuth client id (defaults to `lifecycle-cli`). |
+
+Manage profiles without editing the file by hand:
+
+```sh
+lfc config list # all profiles
+lfc config get # active profile as JSON (no secrets)
+lfc config add-profile staging --api-url https://lifecycle-staging.example.com --issuer
+lfc config use-profile staging # switch the active profile
+lfc --profile staging builds list # one-off profile override
+```
+
+### Environment variables
+
+| Variable | Effect |
+| ------------------- | --------------------------------------- |
+| `LIFECYCLE_API_URL` | Override the API base URL. |
+| `LFC_PROFILE` | Select the active profile by name. |
+| `LFC_JSON=1` | Force JSON output globally. |
+| `LFC_CONFIG_DIR` | Override the config directory location. |
+
+One-off usage with no saved config (auth-less deployments only):
+
+```sh
+lfc --api-url http://localhost:8080 builds list
+```
+
+## Commands
+
+### Authentication
+
+```sh
+lfc login # SSO in the browser
+lfc login --device # headless: prints a URL + code to confirm elsewhere
+lfc whoami # logged-in user, roles, token expiry
+lfc logout
+```
+
+### Builds (preview environments)
+
+```sh
+lfc builds list [--mine] [--search ] [--all] [-n ] [-p ]
+lfc builds get [--manifest]
+lfc builds find --pr [--repo myorg/myrepo] # resolve a PR/branch → build
+lfc builds find --branch --repo myorg/myrepo
+lfc builds status [--watch] # exit 0 deployed, 1 failed, 2 timeout
+lfc builds redeploy [--watch]
+lfc builds destroy --yes
+lfc builds update-uuid
+lfc builds set --static | --no-static | --track-defaults | --no-track-defaults
+lfc builds webhooks [--invoke]
+lfc builds open [--print] # open in the Lifecycle UI
+```
+
+`builds find` resolves a build from a PR or branch — useful when you have a PR
+URL but not the uuid. A PR URL is self-contained; a bare PR number or a branch
+needs `--repo`. With `--json` it returns `{ found, build, matches }` (more than
+one `matches` entry means the selector was ambiguous). Exit `0` found, `3` no
+build yet (e.g. the PR was just opened), `1` if the scan was truncated before
+resolving.
+
+Per-build environment-variable overrides:
+
+```sh
+lfc builds env get
+lfc builds env set KEY=VALUE [KEY=VALUE ...] [--init]
+lfc builds env unset KEY [KEY ...]
+```
+
+### Services (within a build)
+
+```sh
+lfc services list [--all]
+lfc services redeploy
+lfc services enable | disable
+lfc services set-branch
+lfc services history # build/deploy job history
+lfc services logs [--deploy] [--job ] [-f]
+```
+
+### Pods & runtime logs
+
+```sh
+lfc pods list [--service ]
+lfc pods logs [-c ] [-f] [--tail ] [--timestamps]
+```
+
+### Schema validation
+
+```sh
+lfc schema validate [path] # finds lifecycle.yaml locally (offline)
+lfc schema validate --repo myorg/myrepo --branch # server-side check
+```
+
+Exit `0` valid, `1` invalid (per-field errors printed), `3` no config file found.
+
+### Static sites
+
+```sh
+lfc sites create [--name