# CLI (/docs/cli)

> The plico commands.

Run `plico --version` to print the installed version. Every command takes an optional project `[path]` and defaults to the current folder.

| Command | What it does |
| --- | --- |
| `plico validate [path]` | Validates a project and prints diagnostics. It warns about files in `evals/` that `plico eval` would skip, such as `case.json`. |
| `plico run [--message <text>] [path]` | Runs the agent with its configured model. |
| `plico run --dry [--persist] [--script <file>] [path]` | Runs with a scripted model, so it needs no key. |
| `plico runs [path]` | Lists saved runs. |
| `plico events <run-id> [--after <sequence>] [path]` | Prints a run's event log. |
| `plico cancel <run-id> [path]` | Cancels an active persisted run. |
| `plico eval [path]` | Runs the project's scripted evals. |
| `plico readiness [path]` | Reports readiness diagnostics. |
| `plico serve [path]` | Starts the HTTP API. |

`plico <command> --help` prints that command's usage.

Common flags: `--json` prints machine-readable output, and `--db <path>` points at a database other
than `.plico/plico.sqlite`. `plico run` without `--dry` needs a configured model; see
[Model providers](/docs/providers). A dry run prints its events but only saves them with
`--persist`.

## Scripts

`plico run --dry --script <file>` replays a script instead of calling a model. Without `--script`, a dry run uses a
built-in script that calls no tools. A script is a JSON
array of steps:

```json
[
  { "type": "assistant.output", "content": "I will create the ticket." },
  { "type": "tool.call", "toolName": "create_ticket", "arguments": { "title": "VPN down" },
    "result": { "ticketId": "T-1" } },
  { "type": "approval.decide", "approved": true },
  { "type": "artifact.write", "files": [{ "path": "summary.md", "content": "# Ticket" }] }
]
```

| Step | Fields |
| --- | --- |
| `assistant.output` | `content`, the text the agent says. |
| `tool.call` | `toolName`, `arguments`, and an optional `result`. `plico run --dry` runs the real handler. [Evals](/docs/evals) and scripted runs over HTTP use `result` instead and never run it. `"live": true` runs the handler there too. |
| `approval.decide` | `approved`, `true` or `false`. Place it right after a `tool.call` that needs approval. |
| `artifact.write` | `files`, each with `path`, `content` and an optional `mimeType`. |

A script that calls a tool needing approval and has no `approval.decide` after it stops with
`run.waiting`, prints `Dry run waiting for approval.` and exits with code 2. Add `--persist` to save
the run and print its id. See [Approvals](/docs/approvals#try-it-without-a-key).

`--script` also accepts an [eval case](/docs/evals#the-case-file): an object with the steps under
`script`.

### Exit codes

`plico run` exits 0 when the run completed, 2 when it is waiting for approval and 1 on any failure.
`plico validate`, `plico eval` and `plico readiness` exit 0 on success and 1 otherwise.

## serve

`plico serve --studio <project>` also serves the bundled Studio. `--host` and `--port` choose the
address; the default is `127.0.0.1:3000`. Other options:

- `--allow-remote` exposes the server on another host. See [HTTP API](/docs/http-api).
- `--allow-script` lets API callers send scripted runs. Their tool steps use the output in the script and never run handlers. See [HTTP API](/docs/http-api).
- `--inspect` serves for inspection beside a backend that owns the database: nothing is recovered or resumed and every change is refused. See [Use Plico from your backend](/docs/backend).

## eval

`plico eval [path]` runs every `evals/*.script.json` case with no model and exits 1 when a case fails
or when there are no cases. `--allow-empty` accepts an empty `evals/` folder. See [Evals](/docs/evals)
for the case format, approval cases and `--from-run`.

## Help and unknown flags

`plico validate --help` shows validation usage. Unknown flags are reported as errors.
`plico runs` reports clearly when a project has no saved runs.