# partiri CLI — agent guide
This document is shipped inside the `partiri` binary and printed by `partiri llm guide`.
It tells an LLM agent (Claude Code, Codex, custom MCP client, etc.) everything needed to drive
the CLI without reading source.
## 1. Quickstart
```sh
partiri auth --key "$PARTIRI_KEY" # one-time setup
partiri -j llm doctor # confirm environment is sane
partiri -j validate --remote # gate before create
partiri -j -y service create
partiri -j -y service deploy
```
- `-j` switches to JSON output.
- `-y` skips confirmation prompts on destructive operations (`deploy`, `kill`, `pause`, `unpause`).
- Prompts are auto-skipped when stdin is not a TTY (every Bash-tool invocation, every CI run).
## 2. I/O contract
- **stdout**: exactly one structured result per invocation, terminated by `\n`.
- **stderr**: spinners, progress, prompts, warnings — and the error JSON document on failure.
- Every JSON document carries `"schema_version": "1"`.
Envelopes:
```jsonc
// list
{ "schema_version": "1", "data": [ { ... } ] }
// single resource
{ "schema_version": "1", "data": { ... } }
// successful mutation
{ "schema_version": "1", "ok": true, "message": "…", "data": { … } }
// error (stderr, exit 1)
{
"schema_version": "1",
"ok": false,
"error": {
"code": "401", // HTTP status as string OR literal: validation/auth/network/config/cancelled/conflict/missing_dependency
"message": "Unauthorized",
"hint": "Run 'partiri auth' to update your API key.",
"likely_causes": ["…"],
"suggested_commands": ["partiri auth --key <K>", "partiri llm doctor"]
}
}
```
Exit codes: `0` success, `1` any error, `2` user cancellation (Ctrl-C / inquire abort).
When something goes wrong, **read `error.suggested_commands` first** — that's the next thing to run.
## 3. The `.partiri.jsonc` schema
```jsonc
{
"fk_workspace": "uuid", // string. Required.
"fk_project": "uuid", // string. Required.
"service": {
"name": "max-16-chars", // string ≤16. Required.
"deploy_type": "webservice", // "webservice" | "static" | "private-service". Required.
"runtime": "node", // "node" | "rust" | "python" | "go" | "ruby" | "elixir" | "php" | "jvm" | "dotnet" | "cpp" | "static" | "registry". Required.
"root_path": ".", // string. Required (defaults to ".").
"repository_url": "https://…", // string. XOR with registry_url.
"repository_branch": "main", // string. Required when repository_url is set.
"registry_url": null, // string|null. XOR with repository_url.
"registry_repository_url": null, // string|null. Required when registry_url is set.
"fk_service_secret": null, // string|null UUID. Required for private repo/registry.
"build_path": null, // string|null. Output dir of the build step.
"build_command": "npm run build", // string|null. Required for repo source on non-static runtimes.
"pre_deploy_command": null, // string|null. Runs before each deploy (e.g. migrations).
"run_command": "node ./dist/server", // string|null. Required for webservice / private-service.
"fk_region": "uuid", // string. Required.
"fk_pod": "uuid", // string. Required.
"health_check_path": "/health", // string|null. Path or absolute URL. Probed by `validate --remote` only when absolute.
"maintenance_mode": false,
"active": true,
"env": [ { "key": "NODE_ENV", "value": "production" } ]
}
}
```
Mutual exclusions and required-field rules are encoded in `partiri llm schema --json` (machine-readable).
## 4. Discovery commands
| Everything | `partiri -j llm context` | — |
| Workspaces | `partiri -j workspaces list` | — |
| Projects | — | `partiri -j projects list --workspace <UUID>` |
| Regions | — | `partiri -j regions list --workspace <UUID>` |
| Pods | — | `partiri -j pods list --workspace <UUID>` |
| Services | — | `partiri -j services list --project <UUID>` |
`partiri llm context` is the single most useful command for any multi-resource decision — it
fans out the per-workspace requests in parallel and returns a fully nested tree.
## 5. Workflow recipes
### Create a new service from scratch
```sh
partiri auth --key "$KEY"
partiri init --template # writes .partiri.jsonc with commented examples
# (edit .partiri.jsonc — fill in fk_workspace / fk_project / fk_region / fk_pod and the service.* block)
partiri -j -y service create
partiri -j -y service deploy
```
### Adopt an existing service into a fresh checkout
```sh
partiri auth --key "$KEY"
partiri service pull # interactive; or write .partiri.jsonc by hand with id set
partiri -j -y service deploy
```
### Deploy across many directories (the "8 services" scenario)
```sh
( cd "$dir" \
&& partiri init --template \
&& jq -r '...' /tmp/ctx.json | apply_to .partiri.jsonc \
&& partiri -j validate --remote \
&& partiri -j -y service create \
&& partiri -j -y service deploy )
done
```
### Move a service to a different region/pod
```sh
partiri -j service link --region <UUID> --pod <UUID>
partiri -j -y service push
partiri -j -y service deploy
```
### Wire up a private repo
```sh
partiri -j validate --remote # should now succeed
```
## 6. Error code catalog
Same content as `partiri llm errors --json`:
| `400` | Bad request — config values out of range / wrong type | `partiri validate` |
| `401` | Unauthorized — API key missing/expired/revoked | `partiri auth --key <K>` |
| `402` | Insufficient workspace balance | Top up at https://partiri.cloud/settings/billing |
| `403` | Permission denied or workspace limit reached | `partiri llm whoami` |
| `404` | Resource not found | `partiri llm context` to see real UUIDs |
| `409` | Conflicting operation in progress | `partiri service jobs` |
| `422` | Invalid request data / schema mismatch | `partiri validate --remote` |
| `429` | Rate limited | wait and retry |
| `500-599` | Server error | retry; if persistent, contact support |
| `auth` | No API key configured | `partiri auth --key <K>` |
| `validation` | Local config validation failed | `partiri llm next` |
| `network` | API host unreachable | `partiri llm doctor` |
| `config` | Bad `.partiri.jsonc` content | `partiri validate` |
| `cancelled` | User aborted (Ctrl-C / inquire cancel) | exit 2 |
| `missing_dependency` | A required predecessor wasn't done | `partiri llm next` |
## 7. Common pitfalls
- **`service.name` must be ≤16 characters.** Validated locally; the API also rejects longer names.
- **`fk_region` and `fk_pod` must come from the same workspace.** Cross-workspace UUIDs return 404.
- **`repository_url` XOR `registry_url`.** Setting both errors out at `validate`.
- **Private repositories and registries require `fk_service_secret`.** Without it, `validate --remote` fails on the source-reachability check; create the secret in the Partiri dashboard, list it via `partiri llm context`, then set it with `partiri service token --secret <UUID>`.
- **`health_check_path` accepts either a path or an absolute URL.** Only absolute URLs are probed by `validate --remote`; relative paths are deferred to runtime.
- **`deploy_tag` is set by the deploy job once it succeeds — not synchronously by `service deploy`.** The deploy is async, so the tag may still be empty right after the POST returns. `service deploy` does a best-effort refresh; if the job is still in progress, run `partiri llm next` (which inspects job status) or `partiri service pull` to refresh later. Required for `partiri service logs` and metrics.
- **`init --template` refuses to overwrite an existing `.partiri.jsonc`.** Delete the file manually first (or pull the existing service).
## 8. Glossary
- **Workspace** — billing/ownership boundary. A user belongs to one or more workspaces.
- **Project** — a logical grouping of services within a workspace, with an environment label (`dev`/`staging`/`prod`).
- **Service** — the deployable unit. One service per `.partiri.jsonc` per directory.
- **Region** — geographic location. Pods live in regions.
- **Pod** — a sized compute slot (CPU + RAM + replicas). Pick a pod that matches your service's needs.
- **deploy_tag** — the immutable tag of the most recent successful deploy. Used to fetch logs/metrics for that exact build.
- **fk_*** — foreign-key fields in `.partiri.jsonc` pointing at other resources by UUID.