---
title: "CLI for AI agents"
description: "Discover commands, parse JSON output, handle exits, and write idempotent simple-plm scripts."
---

The `plm` CLI is designed for AI agents as a first-class user. Every command has stable JSON output, predictable exit codes, and idempotent semantics. This page covers the patterns that make agent-written automation reliable.

## Discovery

Three discovery surfaces, in increasing detail:

1. **`/llms.txt`** — a curated index of every docs page, one bullet per page. Good for "what *is* this product?"
2. **`/llms-full.txt`** — the entire docs corpus concatenated in markdown. Stream once at the start of a session and you have everything.
3. **Per-page markdown** — every `/docs/<slug>/` has a `.md` alternate at `/docs/<slug>.md` and a `<link rel="alternate" type="text/markdown">` in the `<head>`.

For per-command machine-readable structure, each [CLI reference page](/docs/cli/) has an `<details>Agent-readable summary</details>` block containing JSON with arguments, options, defaults, and subcommands.

`plm <noun> --help` works too, but tends to be terser than the docs.

## Output

```bash
plm parts list --format json
```

`--format json` is the agent default. Schemas are stable per command and versioned in the per-command reference page. `--format table` and `--format yaml` are also supported.

## Exit codes

| Code | Meaning |
|------|---------|
| `0` | Success. |
| `1` | User error (bad flag, validation failure, missing argument). |
| `2` | Network or auth failure. The credential at `~/.config/plm/credentials` may need refresh — try `plm login` again. |
| `3` | Conflict — a write-time collision in `plm-data/`. Retry after pulling the latest state. |

Agents should distinguish exit `1` (don't retry, fix the call) from `2`/`3` (transient, retry with backoff).

## Idempotency

Writes are addressed by deterministic IDs (part numbers, BOM revision hashes, ECO IDs). Re-running the same `plm parts create --file foo.yaml` is a **no-op** if the existing part already matches the input bit-for-bit. If it differs, you get exit `3` unless you pass `--update`.

This means an agent can safely retry a failed write without worrying about creating duplicates.

## Authentication for headless agents

For non-interactive contexts (CI, scheduled tasks, agent runners):

```bash
plm login --token <PAT>
```

PATs are generated in the dashboard at **Settings → API tokens**. Set the `PLM_TOKEN` env var to pre-populate, then `plm login --token "$PLM_TOKEN"`. Tokens are scoped to one org by default; pass `--org` at command time to operate on others if your PAT has cross-org permission.

## Where the data lives

`plm-data/` in a Git repo, one repo per org. Branch + commit semantics are standard Git. Agents that want raw read access can clone the org's `plm-data` repo directly — the CLI exists for write paths and for surface area that's expensive to reimplement (validation, indexes, ECO state).

## A sample agent loop

```python
import subprocess, json

# 1) Discover the surface.
corpus = subprocess.check_output(["curl", "-fsSL", "https://simple-plm.com/llms-full.txt"]).decode()

# 2) Read a per-command JSON summary from the reference page.
ref = subprocess.check_output(["curl", "-fsSL", "https://simple-plm.com/docs/cli/parts.md"]).decode()

# 3) Execute. JSON output, retry on exit 2/3.
def call(args, retries=3):
    for _ in range(retries):
        r = subprocess.run(["plm", *args, "--format", "json"], capture_output=True, text=True)
        if r.returncode == 0:
            return json.loads(r.stdout)
        if r.returncode in (2, 3):
            continue
        raise RuntimeError(f"plm {args} → exit {r.returncode}: {r.stderr}")
    raise RuntimeError("retries exhausted")

print(call(["parts", "list"]))
```
