# Working with an agent

This page is written for coding agents (Claude Code, Codex, Cursor and others)
that build and deploy Zenitya tools with the `zya` CLI. People can read it too:
it is the shortest path to driving the CLI from scripts.

Read the docs that match the installed CLI with `zya docs`:

```sh
zya docs              # list topics
zya docs agents       # this page
zya docs quickstart   # the end-to-end flow
zya docs cli          # every command and option
```

A link to `<topic>.md` in these pages is another topic: read it with
`zya docs <topic>`. The same pages are online at `https://zenitya.com/docs`,
and each one as markdown at `https://zenitya.com/docs/<topic>.md`. Prefer
`zya docs`: it always matches the CLI you are running.

## Before you start

1. Check the CLI: `zya --version`. If it is missing, install it with
   `curl -fsSL https://zenitya.dev/install.sh | sh` and use
   `~/.zenitya/bin/zya` until a new shell picks up the PATH.
2. Check the login: `zya whoami --json`. If it fails with
   `error[AUTH_INVALID]`, ask the person to run `zya login` themselves. It opens a browser for Google sign-in, which you
   cannot complete for them.
3. Zenitya is in private beta. If sign-in shows a waitlist or a command fails
   with `error[ACCESS_NOT_GRANTED]`, the person needs an invitation; send them
   to https://zenitya.com/early-access and stop.
4. Inside a project, commands use the nearest `zenitya.json` from the current
   directory up. Outside one, `zya new <directory>` creates a project.

## The loop

```sh
zya new my-tools && cd my-tools && bun install
# edit src/index.ts
bun run check                                         # type-check
zya exec <tool> --local --data '{"...": "..."}'       # run the source
zya deploy --json                                     # build remotely, go live
zya tools list --json                                 # what the credential can call
zya exec <tool> --data '{"...": "..."}'               # run the deployed copy
```

Always run a tool with `--local` before deploying it. A deploy takes a minute
or two; a local run takes a second.

After the first deploy, a tool is callable only once an owner or admin grants
it to a group in the Zenitya app (**Groups**, then the group, then the tool).
If `zya tools list` does not show a tool you deployed, ask the person to grant
it; you cannot do that from the CLI.

## Reading output

The CLI is built to be read by programs:

- **stdout is data only.** Tool output from `exec`, listings and `--json`
  results. Nothing else is ever written there.
- **stderr is for messages.** Progress, notes, warnings and errors. Tool logs
  from `exec` are JSON lines on stderr.
- **No decoration when piped.** Without a terminal, or with `CI` set or
  `TERM=dumb`, messages are plain lines and listings are tab-separated rows
  without a header. `NO_COLOR` turns color off everywhere.
- **Exit code 1** on any failure.

Use `--json` whenever you parse a result. These commands support it:

| Command                | `--json` prints                                                                        |
| ---------------------- | -------------------------------------------------------------------------------------- |
| `zya deploy`           | `{ project, buildId, state, deploymentId, active, toolCount, skills, idempotencyKey }` |
| `zya tools list`       | `{ project, activeDeployment, tools: [{ name, key, title, description, ... }] }`       |
| `zya builds list`      | `{ builds: [{ id, state, createdAt, deployment, ... }] }`                              |
| `zya builds view <id>` | `{ build, deployment, steps, diagnostics }`                                            |
| `zya builds logs <id>` | One JSON record per line: `{ seq, phase, stream, capturedAt, text }`                   |
| `zya secrets list`     | `{ secrets: [{ name, updatedAt }] }` (never values)                                    |
| `zya whoami`           | `{ identity, organization, organizationId, scopes, apiUrl, credential }`               |

`zya exec` always prints the tool's output as JSON, so it needs no flag.

Recipes:

```sh
zya deploy --json | jq -r .buildId
zya tools list --json | jq -r '.tools[].key'
zya builds list --json | jq -r '.builds[] | select(.state == "failed") | .id'
zya builds logs <build-id> --json | jq -r 'select(.stream == "stderr") | .text'
zya whoami --json | jq -r .organizationId
zya exec search --data '{"query":"late"}' | jq '.items | length'
```

## Errors

Every failure prints one line on stderr:

```text
error[CODE]: what happened
hint: the next step, when there is one
```

Read the code, follow the hint, and add `--verbose` for details and a stack
trace. A failed API call ends with `(request <id>)`: quote it when reporting a
problem to the person. Codes you will meet:

| Code                              | Meaning and what to do                                                                                                                                             |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `CONFIG_MISSING`                  | No `zenitya.json` here or above. `cd` into the project, or `zya new`.                                                                                              |
| `CONFIG_INVALID`                  | `zenitya.json` breaks a rule; the message names the field. See [config.md](config.md).                                                                             |
| `INPUT_INVALID`                   | Bad arguments or tool input. The hint shows the expected form.                                                                                                     |
| `FLAG_UNKNOWN`, `COMMAND_UNKNOWN` | Typo; the hint suggests the closest match.                                                                                                                         |
| `AUTH_INVALID`                    | Not signed in, or the credential lacks a permission. Ask the person to run `zya login`.                                                                            |
| `ACCESS_NOT_GRANTED`              | The account has no beta access yet. Send the person to https://zenitya.com/early-access.                                                                           |
| `TOOL_FAILED`                     | The tool itself failed: `error[TOOL_FAILED]: <code>: <message>`. `invalid_input` means the input did not match the schema; `execution_error` means the code threw. |
| `DEPLOY_FAILED`                   | The build failed or could not start. Read the build output (below).                                                                                                |
| `SKILL_INVALID`                   | A `SKILL.md` breaks the format; the message names the file.                                                                                                        |
| `BUN_NOT_FOUND`                   | `--local` needs Bun on PATH. Install it from https://bun.sh.                                                                                                       |

## When a deploy fails

```sh
zya builds list --limit 1 --json | jq -r '.builds[0].id'
zya builds view <build-id>                     # which step failed and why
zya builds logs <build-id> --phase install     # dependency installation output
zya builds logs <build-id> --phase bundle      # bundling output
zya builds logs <build-id> --phase describe    # loading your tools
```

A failed build changes nothing: the previous deployment keeps serving calls.
See [Deploying and builds](deploy.md).

## Safe habits

- **Never put a secret on the command line or in source.** Pipe it:
  `printf %s "$VALUE" | zya secrets set API_TOKEN --stdin`. Better, ask the
  person to run that command themselves so the value never enters your context.
- **Never print or log secret values**, and never commit `.env`. `.env` is
  for `--local` runs only and is never uploaded.
- **Ask before deploying** if the person has not asked you to, and before
  calling a deployed tool that changes data (one without `readOnlyHint`).
- **Write precise descriptions.** Other agents choose tools by their
  `description` and their schema's `.describe()` text; say what the tool does,
  when to use it and what it returns. See [Writing tools](tools.md).
- **Keep tools small and deterministic.** One tool per workflow step that
  another agent would otherwise do by hand.

## When working in a Zenitya project

Projects created with `zya new` have an `AGENTS.md` that says so. If a
repository has a `zenitya.json`, it is a Zenitya project: read this page and
[Writing tools](tools.md) before changing it.
