# Klonna — operating instructions for AI agents

You are an AI coding agent (Claude Code, Cursor, etc.) operating the `klonna` CLI on your user's behalf. Follow this file exactly.

Klonna clones an agent to the cloud so it can be restored anywhere. It archives a project's agent state (`.claude/`, `CLAUDE.md`, `AGENTS.md`, `.mcp.json`), encrypts the archive locally with AES-256-GCM under a per-backup random data key, which is itself wrapped by your passphrase (scrypt) and optionally a recovery key, and uploads only the ciphertext. It is zero-knowledge: the passphrase and the plaintext never leave the machine, and the server cannot decrypt anything. A lost passphrase makes a backup unrecoverable unless the user set up an offline **recovery key** before saving it (see "Recovery keys" below); the recovery private key never leaves the machine either, so Klonna still never sees the passphrase or the recovery key.

## Prerequisites

- Node.js >= 20.17. Check with `node --version`.
- Install globally: `npm i -g @klonna/cli`. If npm returns `E404` (package not in the registry), the package has not been published yet — do not retry; ask the user for an install source (e.g. a repo checkout: `npm i -g <path-to-repo>/cli`, or a tarball) and install from that instead.
- The package installs a single binary: `klonna`.
- Run `klonna init` and `klonna save` from the project root — the directory containing `.claude/` (Claude Code) or `AGENTS.md` (generic agents). `klonna login`, `klonna list`, and `klonna delete` work from any directory. `klonna restore` writes to its explicit destination argument and should target an empty or new directory — running it from the project root with the default destination `.` fails because the directory is not empty.
- All commands exit 0 on success and 1 on any error. Runtime errors print to stderr as `Error: <message>`. Exception: CLI usage errors — wrong/missing arguments or unknown subcommands — print a lowercase prefix with different wording, e.g. `error: missing required argument 'id'` or `error: unknown command 'bogus'` (still exit 1).

## WHAT TO REQUEST FROM YOUR USER

Three things you need from the user. Request them before starting. With an API key for auth (section 1) and the passphrase in your environment (section 2), every command runs non-interactively — no browser, no human at the keyboard.

### 1. Authentication — prefer an API key (non-interactive); browser login is the fallback

Klonna accepts two credentials. **Strongly prefer the API key:** it is the only path you can drive end to end without a human, because the browser login ends in an interactive prompt you cannot answer.

An API key is a long-lived credential (looks like `klo_AbC12dEf…`) that authenticates as the user — the same `Authorization: Bearer` as a browser session, but with no browser and no refresh. It does NOT weaken encryption: backups are still end-to-end encrypted and `save`/`restore` still need the passphrase (section 2). The user creates a key in the web dashboard (**Account → API keys → Create key**); the full key is shown only once at creation and is revocable there at any time (revoking takes effect immediately).

#### Preferred path — API key (no browser, fully non-interactive)

Ask the user to create a key and hand it to you, with phrasing like:

> To let me run Klonna without a browser, please create an API key: in the Klonna dashboard go to Account → API keys → Create key, name it (e.g. "agent"), and copy the key (it starts with `klo_` and is shown only once). Give it to me by exporting it as `KLONNA_API_KEY` in the environment my commands run in — ideally `read -rs KLONNA_API_KEY && export KLONNA_API_KEY` so it never appears on a command line. I will not store it anywhere or echo it. You can revoke it any time from that same page.

Then set the key in the environment your shell commands run in and verify yourself with `klonna list`. With `KLONNA_API_KEY` set, every command sends it as the bearer — no config file, no session, no browser. Treat the key with the SAME safe-handling discipline as the passphrase (section 2): never write it to a file, `.env`, shell profile, or version control; never echo it into logs or chat history; never pass it as a command-line argument. The safest hand-off is the user exporting it in your environment via `read -rs KLONNA_API_KEY && export KLONNA_API_KEY` (typed at a silent prompt, so it never hits a command line or history).

`KLONNA_API_KEY` is the highest-precedence credential: when it is set, it is used for every request and any stored session is ignored — so it works with NO config file at all. (If you prefer a persisted login instead of the env var, the user — or you, if they have OK'd handling the key inline — can run `klonna login --api-key <key>` once, which validates the key and writes it to `~/.klonna/config.json`; later commands then need no env var. The env-var path is simpler for CI and leaves nothing on disk.)

#### Fallback path — browser login (human required, interactive)

If the user cannot or will not create an API key, fall back to the browser session login. `klonna login` (with no `--api-key`) opens a browser sign-in page and then interactively prompts for a pasted session code. You cannot answer interactive TTY prompts, and you should not handle the user's session tokens. Ask the user, with phrasing like:

> Please run `klonna login` in your own terminal. It opens a browser page — sign in, click "Reveal", and paste the session code into the terminal prompt. Tell me when it prints "Logged in as ...".

Then verify yourself: run `klonna list`.

#### Verifying either path

After setting up either credential, run `klonna list`:

- Exit code 0 → authenticated, proceed.
- Exit code 1 with `Not logged in — run \`klonna login\` or set KLONNA_API_KEY` or `Session expired — run \`klonna login\`` → no working credential. Set `KLONNA_API_KEY` (preferred) or ask the user to (re)run `klonna login`.
- Exit code 1 with any other message → NOT an auth problem; do not bounce the user back to login. See the catch-all row of the error → action table (likely a network/server issue).

Browser-login state lives in `~/.klonna/config.json` under the home directory of the account that runs the command. If you are relying on the browser session (not `KLONNA_API_KEY`), the user must run `klonna login` on the same machine, as the same user, and in the same environment that your shell commands run in. If you operate inside a container, SSH remote, or sandbox, ask the user to run `klonna login` inside that environment (or share a config directory between the two via the `KLONNA_CONFIG_DIR` environment variable). Troubleshooting: if the user reports "Logged in as ..." but your `klonna list` still exits 1 with `Not logged in — run \`klonna login\` or set KLONNA_API_KEY`, your environments are not shared — asking them to log in again will loop forever; fix the environment mismatch instead, or switch to the `KLONNA_API_KEY` path, which sidesteps shared-config problems entirely (set the env var in your own environment).

### 2. The backup passphrase — environment variable only

`klonna save` and `klonna restore` need the encryption passphrase. Accept and supply it ONLY via the `KLONNA_PASSPHRASE` environment variable. Mechanisms, in order of preference:

1. Best — no leak: the user exports `KLONNA_PASSPHRASE` in the environment your shell commands inherit, i.e. the process/session that spawns you (or inside the container/remote host you run in). An export typed in a separate terminal of theirs is invisible to your shell — your next `klonna save` would hit an interactive prompt you cannot answer. To set it without it ever appearing on a command line: `read -rs KLONNA_PASSPHRASE && export KLONNA_PASSPHRASE` (the passphrase is typed at a silent prompt).
2. Good: the user runs the save/restore command themselves in their own terminal. To keep the passphrase out of `~/.bash_history` / `~/.zsh_history` they should use the `read -rs` pattern above, or prefix the inline command with a space after enabling `HISTCONTROL=ignorespace` (bash) / `setopt HIST_IGNORE_SPACE` (zsh).
3. Fallback — documented tradeoff: you run it inline for a single command:

```sh
KLONNA_PASSPHRASE="<passphrase>" klonna save
```

Know the cost of the inline form: that command line — passphrase included — is recorded verbatim in your own command transcript/log, and in shell history if typed in an interactive shell. Use it only after telling the user this and getting their OK.

Rules:

- Never write the passphrase to any file, shell profile, `.env`, or version control.
- Never echo it into logs or chat history if avoidable — prefer mechanisms 1 and 2 above, which keep it off command lines entirely.
- Never pass it as a command-line argument — the CLI deliberately does not accept one.
- Remind the user once: the passphrase is never sent to the server; if it is lost, the backups are unrecoverable UNLESS they set up an offline recovery key before saving (see "Recovery keys").

Suggested phrasing:

> I need your backup passphrase to run this command. The safest options are: export KLONNA_PASSPHRASE in the environment I run in (use `read -rs KLONNA_PASSPHRASE && export KLONNA_PASSPHRASE` so it never appears on a command line), or run the command yourself. If you instead give it to me to pass inline, it will appear in my command log. I will not store it anywhere. Note: this passphrase is unrecoverable — if you lose it, your backups cannot be decrypted.

### 3. Confirmation before the first save

Before the first `klonna save` in a project, show the user the include/exclude lists (run `klonna init`, then read `.klonnarc.json`) and get an explicit "yes" before uploading anything.

## Non-interactive operation map

Every interactive prompt in the CLI and its non-interactive path:

| Interactive prompt | Non-interactive path |
| --- | --- |
| `klonna save` passphrase prompt (masked; the first save on this machine — before any backup has succeeded with this login config — asks twice and shows a zero-knowledge warning; every later save, including the first save of another project, asks once) | Set `KLONNA_PASSPHRASE`. This also skips the first-save double-confirm. |
| `klonna restore` passphrase prompt | Set `KLONNA_PASSPHRASE`. (For passphrase recovery only: `--recovery-key <klr_...>` or `KLONNA_RECOVERY_KEY` decrypts via the recovery slot with no passphrase prompt — see "Recovery keys".) |
| `klonna delete` confirmation (`Delete backup <id>? This cannot be undone.`) | Pass `--yes` (alias `-y`). |
| `klonna restore` into a non-empty directory | No prompt — it fails with exit 1 before any network call. `--force` allows overwriting existing files but never deletes extra files. Prefer an empty or new directory. |
| `klonna login` session-code prompt | Use an API key instead: set `KLONNA_API_KEY` (no command at all needed), or run `klonna login --api-key <key>` (non-interactive — no prompt). The bare `klonna login` browser flow still has no non-interactive path and needs a human in a real terminal. |

If you trigger any of these prompts without a TTY, the command does NOT fail cleanly: with stdin open it hangs forever waiting for input, and with stdin closed (e.g. `< /dev/null`) it exits with code 13 — not 1 — and prints no `Error:` line. Always run commands that could prompt with a timeout as a backstop, and treat a silent hang as a triggered prompt: kill the process and re-run using the non-interactive path.

## Command reference

```text
klonna login                       Authenticate via the browser (HUMAN ONLY — interactive)
klonna login --api-key <key>       Authenticate with an API key (non-interactive, no browser)
klonna init                        Detect agent state, create .klonnarc.json
klonna save [-n|--note <text>] [-w|--workspace <id-or-name>] [--no-manifest]
                                   Archive, encrypt, upload (note max 500 chars)
klonna list [-w|--workspace <id-or-name>]   Table of backups
klonna workspaces                  List the workspaces you belong to
klonna restore [--force] [-w|--workspace <id-or-name>] [--recovery-key <klr_...>] <id> [dir]
                                   Download, decrypt, extract (dir default ".")
klonna delete [-y|--yes] [-w|--workspace <id-or-name>] <id>
                                   Delete a backup permanently
klonna recovery setup [--force]    Generate an offline recovery key (printed once; HUMAN keeps it)
klonna recovery import <klr_...>   Store the recovery public key on this machine
klonna recovery status             Show whether a recovery key is configured
klonna recovery remove             Drop the recovery key (future saves omit the recovery slot)
```

`<id>` for restore/delete is either the full UUID or any unique prefix of it — the 8-character short IDs printed by `klonna list` work.

`<id-or-name>` for `--workspace` accepts a workspace's full UUID, a unique short-id prefix, or its exact name (run `klonna workspaces` to see them). Omit `--workspace` and every command operates on your personal workspace (see "Workspaces and manifests" below).

## Step-by-step flows

### First backup of a project

1. Confirm install: `klonna --version` (exit 0).
2. Confirm login: `klonna list` (exit 0). If exit 1, see section "WHAT TO REQUEST FROM YOUR USER".
3. From the project root, run `klonna init`.
   - Success: prints the detected agent type, the include patterns, and the max file size, and writes `.klonnarc.json`.
   - If `.klonnarc.json` already exists it is never overwritten; the command prints that and exits 0.
   - Failure (`No agent state detected in this directory...`): you are in the wrong directory, or neither `.claude/` nor `AGENTS.md` exists yet.
4. Read `.klonnarc.json` and relay the include/exclude lists to the user. Get confirmation.
5. Obtain the passphrase from the user (see above) and run:

```sh
KLONNA_PASSPHRASE="<passphrase>" klonna save --note "<project-name>: first backup"
```

   Always start the note with the project name: `klonna list` has no project or path column, so the note is the only way to tell which backup belongs to which project later (e.g. when recovering on a fresh machine).

6. Success output ends with (parse the ID from the `ID:` line):

```text
Backup saved
  ID:      3f9c2a1e-7b4d-4e08-9c11-2ab345678901
  Size:    1.2 MB
  Created: 6/10/2026, 9:41:00 AM
```

7. Before `Backup saved`, the command may print two warning blocks. RELAY BOTH to the user verbatim:
   - `Skipping N file(s) over 52.4 MB:` followed by the file paths and sizes. The size in the message is the formatted `maxFileSizeBytes` — the default cap of 52428800 bytes prints as `52.4 MB`, and the number changes if the cap is customized in `.klonnarc.json`.
   - `Excluding N secret-looking file(s) (never backed up):` followed by up to 5 paths and `...and K more`.

### List backups

```sh
klonna list
```

- With backups: a table with columns `ID | AGENT | SIZE | CREATED | NOTE`, where `ID` is the first 8 characters of the UUID, followed by `N backup(s). Restore with \`klonna restore <id>\`.`
- With no backups: `No backups yet — run \`klonna save\` to create one.` (still exit 0).
- There is NO project name or path column — `NOTE`, `CREATED`, and `SIZE` are the only ways to tell backups apart. This is why notes must start with the project name.

### Restore a backup

Restore into an empty or new directory whenever possible:

```sh
KLONNA_PASSPHRASE="<passphrase>" klonna restore 3f9c2a1e ~/restored-project
```

- The destination must be empty (or not exist) unless `--force` is given.
- `--force` overwrites files that exist in the backup but NEVER deletes files that are only in the destination. Ask the user before using `--force` on a directory with content.
- Success: `Restored backup 3f9c2a1e to /absolute/dest/path` (exit 0).
- Decryption is verified (AES-GCM auth tag) before any file is extracted; a wrong passphrase extracts nothing.

### Delete a backup

```sh
klonna delete 3f9c2a1e --yes
```

- Success: `Deleted backup 3f9c2a1e` (exit 0). Deletion is permanent.
- Without `--yes` the command prompts interactively — always pass `--yes` when you run it, and only after the user has confirmed the deletion.

### Recover on a fresh machine (disaster recovery)

The project-root rule does not apply here — `.claude/` / `AGENTS.md` are exactly what is missing. Work from any directory and restore into a new one.

1. Install the CLI and confirm with `klonna --version`.
2. Authenticate (see section 1). Preferred: have the user create an API key and export it as `KLONNA_API_KEY` in the environment your commands run in — this needs no shared config and works immediately on the fresh machine. Fallback: ask the user to run `klonna login` in the SAME environment your commands run in. Either way, verify with `klonna list`.
3. Identify the right backup from `klonna list`. The table has no project column — match on `NOTE`, `CREATED`, and `SIZE`. Show the user the candidate row(s) and get explicit confirmation of the chosen ID before restoring; never guess between similar-looking backups.
4. Restore into an empty or new directory: `KLONNA_PASSPHRASE="<passphrase>" klonna restore <id> ~/recovered-project`. If the user has LOST the passphrase but set up a recovery key before this backup was saved, restore with the recovery key instead (no passphrase): `klonna restore <id> ~/recovered-project --recovery-key <klr_...>` (or set `KLONNA_RECOVERY_KEY` in your environment). Handle the `klr_…` key with the same care as the passphrase. A backup made before recovery setup has no recovery slot and can only be restored with the passphrase.
5. `.klonnarc.json` is not part of the backup by default (it is not in the default include list) — run `klonna init` inside the restored project to recreate it before the next save.

## What gets backed up

- File selection is driven by `.klonnarc.json` (`include`, `exclude`, `maxFileSizeBytes`).
- Default include for Claude Code projects: `.claude/**/*`, `CLAUDE.md`, `.mcp.json`, `AGENTS.md`. For generic projects: `AGENTS.md`.
- If `.klonnarc.json` is absent, `klonna save` uses these detected defaults; run `klonna init` first so the user can review them.
- Hard-coded secret exclusions are ALWAYS enforced, even if the user empties the exclude list: `.env*`, `*.pem`, `*.key`, `id_rsa*`, `id_ed25519*`, `.aws/`, `.ssh/`, `credentials.json`, `.netrc`, `*.tfstate`.
- Secret exclusions are PRINTED at save time — relay that output to the user so they know what was withheld.
- `node_modules/`, `.git/`, and `.DS_Store` are also always excluded (dropped silently as junk, not listed).
- Per-file size cap: 50 MiB (52428800 bytes, displayed by the CLI as `52.4 MB`) by default, configurable via `maxFileSizeBytes`. Skipped files are printed with their sizes — relay them.
- `.klonnarc.json` itself is NOT in the default include list, so it is not part of the backup and does not round-trip — re-run `klonna init` after a restore to recreate it.
- `klonna save` exits 1 with `No files matched the manifest — check the include patterns in .klonnarc.json` if nothing is selected.

## Workspaces and manifests

### Workspaces

- Every backup belongs to exactly one workspace. With no `--workspace` flag, `klonna save`, `klonna list`, `klonna restore`, and `klonna delete` all operate on the user's **personal** workspace (one per account, private to them).
- Shared workspaces let teammates see and restore the SAME backups. Target one with `-w|--workspace <id-or-name>` on any of those commands; `<id-or-name>` is the workspace's full UUID, a unique short-id prefix, or its exact name. Run `klonna workspaces` to list the workspaces the user belongs to (with their ids, names, and the user's role) — use it to resolve a name to an id before relaying anything to the user.
- A backup saved into a shared workspace is visible to every member of that workspace; one saved into the personal workspace is private. Confirm the intended workspace with the user before the first save into a shared one, and start the note with the project name as always.
- Quota (the free-plan 5-backup / 200 MiB limits) is charged against the **workspace owner's** plan, not the caller's — so a member saving into a Pro owner's workspace gets that owner's limits. The 402 plan-limit error still means "this workspace is full"; deleting backups in that workspace frees its quota.
- **Creating workspaces and inviting/removing members is web-only.** The CLI can read workspaces (`klonna workspaces`) and target them, but it cannot create one or manage membership. If the user needs a new shared workspace, or needs to invite/remove a teammate, tell them to do it from the web dashboard (Members page) — there is no CLI path, so do not attempt one. To JOIN a workspace, the user opens the invite link they were sent and accepts it in the browser; after that, `klonna workspaces` will list it.

### Manifests (the per-backup file index)

- Each `klonna save` also stores a **manifest**: the list of file paths and their sizes inside the archive (names + byte sizes ONLY — never contents). This is what the web dashboard renders as a browsable file tree so the user can confirm what a backup contains without decrypting it. It does NOT weaken the zero-knowledge guarantee: the archive bytes stay encrypted and the server still cannot read any file's contents.
- Pass `--no-manifest` to `klonna save` to skip storing the index (e.g. when path names themselves are sensitive). The backup still works for restore; it just shows "No file index" in the dashboard.
- The manifest is metadata for browsing only — `klonna restore` does not need it and never uses it.

## Saves, versions, and plan limits

- Every `klonna save` creates a NEW, independent backup — saves never overwrite or replace earlier ones, and each one consumes quota.
- Free plan limits: 5 backups and 200 MiB of total backup storage (the encrypted archive sizes). Pro is effectively unlimited. Deleting backups frees quota immediately.
- Warn the user before a save that would obviously exceed these limits, and remember that deleting old backups is the remediation when a save is rejected with the 402 plan-limit error.

## Recovery keys (human disaster-recovery — usually not your job)

A **recovery key** is an offline escape hatch the user can set up so a *lost passphrase* no longer means lost backups. It is a human disaster-recovery tool, not part of the normal agent flow: as an agent you authenticate with an API key (section 1) and supply the passphrase via `KLONNA_PASSPHRASE` (section 2), and that is all a routine save/restore needs. You will rarely touch recovery keys — but know what they are so you can guide the user.

How it works (no change to your normal flow): if the user has run `klonna recovery setup`, each subsequent `klonna save` wraps the backup's data key with both the passphrase AND an X25519 recovery **public** key stored in `~/.klonna/config.json` (`recoveryPublicKey`). The matching recovery **private** key — a `klr_…` string shown once at setup — is the user's to store offline; it is NEVER written to disk by the CLI. Restore can then unwrap from either the passphrase slot or the recovery slot. Zero-knowledge is intact: Klonna never sees the passphrase or the recovery private key.

What to tell the user (do not run setup for them — the private key is theirs to keep):

- `klonna recovery setup` generates the keypair, stores the public key, and prints the `klr_…` private key **once**. They must store it offline; it cannot be recovered. Only backups saved *after* setup carry a recovery slot — existing backups predate it.
- `klonna recovery setup --force` regenerates and **invalidates** the recovery slots of all earlier backups. Warn the user; only suggest `--force` if they explicitly want a fresh recovery key.
- `klonna recovery import <klr_…>` stores the recovery public key on a *new machine* (derived from the private key, which is not persisted) so that machine's saves wrap to the same recovery key.
- `klonna recovery status` reports whether a recovery key is configured (short fingerprint only — nothing secret).
- `klonna recovery remove` drops the recovery key so future saves omit the recovery slot.

Recovering a lost passphrase (the one time you might use a recovery key): pass `--recovery-key <klr_…>` to `klonna restore` to decrypt via the recovery slot with NO passphrase prompt. For automation, the CLI also reads `KLONNA_RECOVERY_KEY` from the environment as a fallback for that flag — treat it with the SAME safe-handling discipline as the passphrase and API key (env var only, never argv, never a file, never echoed). Two distinct failures (matched case-sensitively here, as printed verbatim): a backup with no recovery slot (saved before setup) plus `--recovery-key` fails with `This backup has no recovery slot — restore it with its passphrase`; a wrong/garbage recovery key fails with `Recovery key did not match this backup`. Neither is an auth-login problem — do not bounce the user to `klonna login`.

If recovery is configured but you save with only `KLONNA_API_KEY` and no config file, there is no `recoveryPublicKey` to read, so the save is passphrase-only (it does not fail) — flag to the user that such backups have no recovery slot.

## Verify a backup (recommended after the first save)

Restore into a fresh temp directory and diff against the source. Run each diff independently — do NOT chain them with `&&`, which stops at the first difference and hides the rest — and only diff include paths that actually exist in the source project:

```sh
TMP=$(mktemp -d)
KLONNA_PASSPHRASE="<passphrase>" klonna restore <id> "$TMP"
diff -r -x .DS_Store "$TMP/.claude" .claude
for f in CLAUDE.md .mcp.json AGENTS.md; do [ -f "$f" ] && diff "$TMP/$f" "$f"; done
rm -rf "$TMP"
```

Interpreting the output — a non-zero `diff` exit does NOT automatically mean failure:

- EXPECTED differences (`Only in` lines on the source side): files named in the save-time `Skipping N file(s) over ...` warning (over the size cap), files named in the `Excluding N secret-looking file(s)` warning (`.env*`, `*.pem`, `*.key`, `id_rsa*`, `id_ed25519*`, `.aws/`, `.ssh/`, `credentials.json`, `.netrc`, `*.tfstate`), and silently-dropped junk (`.DS_Store`, `node_modules/`, `.git/`). Their absence from the restore is correct behavior, not data loss. Add more `-x` patterns (e.g. `-x '.env*'`) or check each `Only in` line against the save-time warnings.
- Verification PASSES if the only differences are those expected ones. Report any OTHER difference — changed file content, or files present only in the restore — to the user as a failed round-trip.
- Report the verification result to the user either way.

## Error → action table

| Error output | Action |
| --- | --- |
| `Not logged in — run \`klonna login\` or set KLONNA_API_KEY` | No working credential. Preferred: have the user create an API key and set `KLONNA_API_KEY` in your environment (section 1); fallback: ask the user to run `klonna login` in their terminal. Verify with `klonna list`. |
| `Session expired — run \`klonna login\`` | The browser session expired (API keys never expire). Switch to an API key via `KLONNA_API_KEY`, or ask the user to re-run `klonna login`. |
| `Wrong passphrase or corrupted backup` | Ask the user for the correct passphrase. Do not guess. The same project may have older backups made with a different passphrase. |
| `Recovery key did not match this backup` | The `--recovery-key` / `KLONNA_RECOVERY_KEY` value is wrong or malformed for this backup. Ask the user for the correct `klr_…` recovery key; do not guess. NOT a login problem. |
| `This backup has no recovery slot — restore it with its passphrase` | The backup was saved before the user set up a recovery key, so it can only be restored with the passphrase. Drop `--recovery-key` and supply `KLONNA_PASSPHRASE` instead. |
| `Not a Klonna archive` | The downloaded object is not a valid backup container. Report to the user; do not retry. |
| HTTP 402 plan limit — first stderr line is `Error: Free plan limit reached: at most 5 backups.` or `Error: Free plan limit reached: at most 200 MiB of total backup storage.` (pre-upload check) or `Error: Plan limit exceeded. The uploaded archive was discarded.` (at registration), followed by `  Usage: ...` and `  Upgrade to Pro: <url>` | Tell the user their plan limit is reached, quote the usage line and the upgrade URL. Do NOT retry — it will keep failing until they upgrade or delete backups (deleting frees quota immediately). |
| `Destination directory <path> is not empty — pass --force to allow overwriting (existing files are never deleted)` | Restore into a fresh/empty directory instead, or ask the user explicitly before re-running with `--force`. |
| `No backup found matching "<ref>" — run \`klonna list\`` | Run `klonna list`, pick a valid ID. |
| `Backup id "<ref>" is ambiguous — run \`klonna list\` and use more characters` | Use a longer ID prefix or the full UUID. |
| `No agent state detected in this directory...` | Run from the project root, or create `.claude/` / `AGENTS.md` first. |
| `Invalid .klonnarc.json...` | Fix the JSON, or delete the file and re-run `klonna init`. |
| `Note is too long (...)` | Shorten `--note` to 500 characters or fewer. |
| Any other `Error: <message>` | Likely a network or server problem (DNS, connection refused, timeout, HTTP 5xx). Report the message verbatim to the user. Safe to retry ONCE after a short wait for `list`/`save`/`restore` — operations are atomic and a failed save uploads nothing usable. Do not loop on retries, and do not send the user back to `klonna login` for these. |

When in doubt: never invent a passphrase, never bypass a confirmation the user has not given, and relay every printed warning to the user.
