# Use the Hydrant CLI

Run Hydrant's agent tools from a terminal or a CI job with npx and an agent key. Tables for people, JSON for scripts, exit codes for both.

The Hydrant CLI calls the same tools your agent does, from a terminal or a CI job. Use it to read the board without opening a browser, or to have a pipeline move an issue when the work it describes lands. It's the [@hydrantdev/cli](https://www.npmjs.com/package/@hydrantdev/cli) package on npm: no dependencies, and it never stores the key.

## Before you start

- An agent key. Create one under **Settings › Agents › Access**; [Agent keys](/help/agents/agent-keys) covers the details. A **Read only** key can run `list`, `view`, `tools` and every read tool. Anything that writes needs **Read and write**.
- Node 20.12 or later. `node --version` tells you.
- The right package name. Bare `npx hydrant` runs someone else's package. Always ask for `@hydrantdev/cli`.

## Steps

### Run it

1. Put the key in the environment. `read -s HYDRANT_KEY` (paste, then Return) keeps it out of your shell history; then `export HYDRANT_KEY`.
2. Run `npx @hydrantdev/cli tools`. That lists every tool the key can call, one line each.
3. If you'll use it often, `npm i -g @hydrantdev/cli` installs the `hydrant` command. The examples below use that name; `npx @hydrantdev/cli` works anywhere `hydrant` does.

`HYDRANT_URL` defaults to `https://hydrant.dev`. `--key` and `--url` override the environment for one run.

### Read issues

```sh
hydrant list                                  # open issues, newest first
hydrant list --status "in progress" --assignee ada
hydrant list --view all --all --q export      # include Done and Canceled
hydrant view 12                               # one issue and its latest activity
```

Names match case-insensitively. An unknown status, person, label, project or milestone lists the valid ones instead of guessing.

### Call any tool

```sh
hydrant tools get_issue                       # one tool's input schema
hydrant call get_workspace
hydrant call list_issues '{"limit":5}'
echo '{"limit":5}' | hydrant call list_issues -
```

Arguments are one JSON object. Only `-` reads them from stdin, so a script with an open pipe never hangs waiting.

Write tools need a request id. Leave it out and the CLI makes one and prints it to stderr. If a write fails partway, retry with `--request-id` and that id, and it lands once. [Writes that don't bite](/help/agents/safe-writes) explains why.

### Script it

`list`, `view` and `call` print compact JSON when piped or with `--json`; `tools` needs `--json`. `hydrant list --json | jq '.issues[].title'` does what it looks like.

In GitHub Actions, keep the key in a repository secret. This step moves an issue to Done; `update_issue` needs the issue's current version, so it reads that first:

```yaml
- name: Mark the Hydrant issue done
  env:
    HYDRANT_KEY: ${{ secrets.HYDRANT_KEY }}
    ISSUE_ID: 00000000-0000-4000-8000-000000000000   # the issue's id
  run: |
    version=$(npx --yes @hydrantdev/cli call get_issue "{\"issueId\":\"$ISSUE_ID\"}" | jq '.data.issue.version')
    npx --yes @hydrantdev/cli call update_issue "{\"issueId\":\"$ISSUE_ID\",\"version\":$version,\"patch\":{\"status\":\"done\"}}"
```

Pin a version, like `@hydrantdev/cli@0.2.0`, if you'd rather upgrade on purpose.

## What you should see

`hydrant list` in a terminal prints a table, in colour unless `NO_COLOR` is set:

```text
#   Status      Priority Assignee     Title
#12 In Progress High     Ada Lovelace Rename the export button
#3  Backlog     Urgent   —            Fix the login copy
```

`hydrant view 12` prints the issue's fields, its description and the five latest activity entries.

`call` prints what the tool returned. Piped, it's one line of JSON; in a terminal, indented. The tool's answer sits under `data`:

```json
{"workspace":{"id":"…","name":"Acme","role":"member"},"kind":"receipt","data":{"issue":{"number":12,"status":"done","version":3}}}
```

A write without `--request-id` also prints one line to stderr, which you keep in case you need to retry:

```text
request id: 980d643b-1d14-4ff4-8f77-1c1a79dfe093
```

`hydrant` alone prints the overview. `hydrant <command> --help` gives an example for every flag that command takes.

## If it goes sideways

Every failure prints one line to stderr: what happened, then what to do. Nothing goes to stdout. Piped or with `--json`, that line is a JSON object with `code`, `message` and `hint`. The exit code says which kind of failure it was:

| Code | Means | The hint says |
| --- | --- | --- |
| 0 | It worked. | |
| 1 | The tool refused: not found, invalid arguments, not allowed for your role, or too large. | What to fix, such as checking the arguments against `hydrant tools <name>`. |
| 2 | Usage: unknown command, flag or name, invalid JSON, or no key. | Run `hydrant <command> --help`, or set `HYDRANT_KEY`. |
| 3 | Key refused, read-only key on a write, origin refused, or the key's owner lost access. | Check the key, use a read-and-write key, or fix `HYDRANT_URL`. |
| 4 | Conflict: someone changed it first. | Reread it and send the current version. |
| 5 | Rate limited, after one automatic retry. | Try again in the number of seconds it gives. |
| 6 | Hydrant is unavailable or unreachable. | Try again. For a write, reuse its `--request-id`. |

- **Exit 2 and "must be an origin".** `HYDRANT_URL` takes an origin only: `https://hydrant.dev`, with no path, no query and no trailing `/api/mcp`. Plain `http` works only for a local server.
- **Exit 3 and "HYDRANT_URL must be the exact origin the key was issued for."** The shape is fine but Hydrant refused it: use the exact origin the key came from, usually `https://hydrant.dev`.
- **Exit 3 on a write with a read-only key.** Create a **Read and write** key under **Settings › Agents › Access** and revoke the old one if nothing else uses it.
- **Exit 5.** A rate-limited request never runs, so the CLI waits and sends it once more when the wait is 30 seconds or less. If that fails too, or the wait is longer, it stops and tells you how long to wait. A command that makes several requests, like `list` across pages, can wait more than once.
- **Exit 6 after a write.** The write may have gone through. Retry with the `--request-id` from stderr; Hydrant applies it once either way.
- **The key leaked.** Keys live in **Settings › Agents › Access**, nowhere else. Revoke it there, now. The CLI never writes the key to disk, but your shell history or CI logs might have it.
- **Something else.** [When an agent gets stuck](/help/agents/troubleshooting) covers the errors the CLI passes through from Hydrant.

Contact: bots@hydrant.dev
