# Mercemur CLI

The Mercemur CLI exists for the work an API cannot do: reading and watching files on
your own machine.

Everything reachable over HTTP is already served by the [API](/api-reference/introduction).
The CLI deliberately does not mirror it. There is no `mercemur products list`, because
`GET /api/v1/products` already exists and a second spelling of it would only drift.

What the CLI adds is the local half:

<CardGroup cols={2}>
  <Card title="Your storefront as files" icon="folder">
    Templates on disk, in your editor, in your own git history alongside the rest of
    your work.
  </Card>
  <Card title="A live edit loop" icon="arrows-rotate">
    Save a file, see the change in a preview link, without publishing anything to
    shoppers.
  </Card>
</CardGroup>

## Install

<CodeGroup>

```bash macOS and Linux
curl -fsSL https://cli.mercemur.com/install.sh | sh
```

```powershell Windows
$v = (Invoke-WebRequest -UseBasicParsing https://cli.mercemur.com/latest/VERSION).Content.Trim()
Invoke-WebRequest -UseBasicParsing "https://cli.mercemur.com/v$v/mercemur_${v}_windows_amd64.zip" -OutFile mercemur.zip
Expand-Archive mercemur.zip -DestinationPath $env:LOCALAPPDATA\Mercemur -Force
```

```bash Pin a version
curl -fsSLO https://cli.mercemur.com/v0.1.6/mercemur_0.1.6_darwin_arm64.tar.gz
curl -fsSLO https://cli.mercemur.com/v0.1.6/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
tar -xzf mercemur_0.1.6_darwin_arm64.tar.gz
```

</CodeGroup>

The script picks the right build for your machine, verifies it against a published SHA-256
**before** unpacking, and refuses to install anything that does not match. It installs to
`/usr/local/bin`; set `MERCEMUR_INSTALL_DIR` to put it somewhere else.

On Windows, add `$env:LOCALAPPDATA\Mercemur` to your `PATH` afterwards. The archive contains a
single `mercemur.exe`.

<Note>
  Versioned URLs are immutable, so `/v0.1.6/...` always serves the same bytes. `/latest/` moves
  with each release, which is why the installer resolves the version first and then downloads from
  the versioned path: a download and its checksum fetched either side of a release would otherwise
  disagree and look like tampering.
</Note>

```bash
mercemur version
```

It is a single static binary. There is no runtime to install beside it and no dependency
tree to keep patched, which is the point: a tool that holds a write-scoped API key should
not also drag a package manager onto the machine that reads that key.

<Warning>
  There is no Homebrew formula, npm package or Scoop manifest, so anything claiming to be one is
  not ours. Downloads come from `cli.mercemur.com` and nowhere else, and every archive has a
  checksum published beside it.
</Warning>

## Log in

The CLI authenticates with the same [API key](/authentication/api-keys) everything else
does. Create one in **Settings, API keys** with the `read_storefront` and
`write_storefront` scopes, then:

```bash
mercemur login --store your-store.mercemur.com
```

The key is read without echoing and stored in your operating system's keychain, not in
a file. It never enters your shell history and never appears in the CLI's output or its
error messages.

<Warning>
  A key with `write_storefront` can change what every shopper of your store sees. Grant
  it only to keys you intend to publish with, and use `mercemur logout` on any machine
  you no longer control. Logging out removes the local copy; it does **not** revoke the
  key. If a key may have been exposed, rotate it in the dashboard as well.
</Warning>

On a machine with no keychain, such as a CI runner, set `MERCEMUR_TOKEN` in the
environment instead of running `login`.

## Point it at a store

Every command needs to know which store it is talking to. Four places are checked, and
the first one that names a value wins, **per setting**:

| Order | Source | Example |
|---|---|---|
| 1 | Flag | `--store acme.mercemur.com`, `--api https://...` |
| 2 | Environment | `MERCEMUR_STORE`, `MERCEMUR_API` |
| 3 | Project file | `.mercemur.json` in the working directory |
| 4 | Global config | `mercemur/config.json` in your user config directory |

Per setting rather than per layer: a project file naming the API base is still used when
you pass `--store` on the command line.

Run `mercemur theme info` to see which layer won for each setting.

```json .mercemur.json
{
  "store": "acme.mercemur.com"
}
```

<Note>
  Never put an API key in `.mercemur.json`. It is a file people commit. Keys live in the
  keychain, which is why the CLI has no config field for one.
</Note>

## When things go wrong

Transient failures are retried for you. A `429`, a `5xx` or an unreachable API is tried up to
three times, honouring `Retry-After` when the server sends one.

<Note>
  Retries reuse the **same** idempotency key, which is what makes them safe. If the first attempt
  actually reached the server before the connection dropped, the second is recognised as the same
  write rather than applied twice. So a slow command is often a retry in progress, not a hang.
</Note>

Nothing else is retried. A `409` means somebody changed the resource under you and sending the
same request again cannot succeed; a `401` means the key was rejected and hammering it is how an
account gets locked.

Ctrl-C stops cleanly. `theme dev` and `webhook tail` finish what they are doing and exit rather
than being killed mid-upload, and the same applies to `SIGTERM` so a container or CI runner
stopping the process does not have to escalate.

## Shortcuts

| Short | Long |
|---|---|
| `-s` | `--store` |
| `-j` | `--json` |
| `-p` | `--path` |
| `--version` | same as `mercemur version` |

`th` and `wh` are aliases for `theme` and `webhook`.

Shell completion is built in:

```bash
mercemur completion zsh > "${fpath[1]}/_mercemur"
mercemur completion bash > /etc/bash_completion.d/mercemur
```

## Scripting it

Every command accepts `--json` and emits a single machine-readable document with no
human-readable lines mixed in, so it composes:

```bash
mercemur theme pull --json | jq -r '.templates'
```

Exit codes distinguish the cases a script has to treat differently:

| Code | Meaning | What to do |
|---|---|---|
| `0` | Success | |
| `1` | Failure | Read the message |
| `2` | Wrong invocation | Fix the command |
| `3` | Key rejected or missing | Do not retry, replace the key |
| `4` | Something changed under you | Pull and re-apply, do not retry |
| `5` | Not found | |

A retry loop that treats every failure alike will spin forever on a revoked key. That is
why `3` and `4` are separate from `1`.

## Next

<CardGroup cols={2}>
  <Card title="Theme workflow" icon="code" href="/cli/theme">
    Pull your storefront to disk, edit with live preview, and publish.
  </Card>
  <Card title="Webhook workflow" icon="bolt" href="/cli/webhooks">
    Send test events, watch deliveries arrive, and replay failures.
  </Card>
</CardGroup>
