Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 53 additions & 8 deletions skills/agenteye-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ description: |-

# AgentEye CLI

`agenteye` is a command-line client for an AgentEye deployment. It talks only to
the dashboard `/api/*` over an authenticated session — you never hit the backend
directly. Every command takes `--json`, so it's built to be driven by an agent.
`agenteye` is a command-line client for an AgentEye deployment. It authenticates
either as a signed-in **user** or with a scoped **API key** (§2), and every command
takes `--json`, so it's built to be driven by an agent.

## 1. Find how to invoke it

Expand All @@ -42,7 +42,30 @@ Throughout this skill, `agenteye` means "whichever form you resolved."

- **Global options go BEFORE the command:** `agenteye --json events`, never
`agenteye events --json`. Globals are `--base-url`, `--org`, `--token`,
`--json`, `--insecure`/`--secure`. After the command they're a usage error.
`--api-key`, `--json`, `--insecure`/`--secure`. After the command they're a
usage error.
- **Two ways to authenticate, and they are not interchangeable:**

| | How you supply it | What it is |
|---|---|---|
| **Session** | `agenteye login` (interactive; it emails a one-time code) | a signed-in **user**, carrying that person's org memberships and permissions |
| **API key** | `--api-key <key>`, or `AGENTEYE_CLI_API_KEY` in the environment | a scoped **credential**, carrying exactly the permissions it was granted |

A key is what you want in CI or any other non-interactive context: no browser, no
emailed code, nothing to expire mid-run. `AGENTEYE_CLI_API_KEY` takes precedence
over `AGENTEYE_CLI_TOKEN`; supplying both `--api-key` and `--token` is a usage
error (exit 2) rather than a silent guess about which you meant. **A key is never
written to the CLI's saved config** — pass it every time, from the environment.
`--api-key ""` means "no override" and does **not** fall back to a saved session.

- **Some commands need a signed-in user.** `login`, `logout`, `orgs *` and the
whole `agent` group refuse a key with a usage error (**exit 2**) and make **no
network call at all** — there is no user to sign in, no saved active org to
switch, and no private assistant thread to own. `keys update` is the near miss
with a different signature: it *does* reach the server and comes back **exit
5**, because re-scoping a key needs a permission that can never be granted to a
key. Either way the key is not the problem to fix — plan around them rather than
retrying or hunting for a flag.
- **Default to `--json` and parse it.** It prints clean JSON to stdout and
nothing else. The plain output is a boxed Rich UI meant for human eyes — it
burns context with box-drawing characters and is awkward to parse. Use the
Expand All @@ -57,17 +80,20 @@ Throughout this skill, `agenteye` means "whichever form you resolved."
| 1 | unexpected / server error | report it to the user |
| 2 | usage error (bad flags/args) | fix the command and retry |
| 3 | can't reach the dashboard | check base-url / connectivity |
| 4 | not signed in / session expired | user must run `agenteye login` |
| 4 | no usable credential — not signed in, session expired, **or the API key was rejected** | session: user must run `agenteye login`. Key: it's missing, mistyped, disabled, or belongs to another deployment — don't retry, and don't fall back to a session |
| 5 | authenticated but missing permission | message names the exact permission |
| 6 | resource not found | the named resource doesn't exist |

## 3. First call: confirm you're connected

Before real work, run `agenteye --json whoami` and react to the exit code:

- **exit 4** → not signed in. Tell the user to run `agenteye login` (it emails a
one-time code and prompts interactively — you can't complete it for them, and
don't fabricate a token).
- **exit 4** → no usable credential. If the user is working from a session, tell
them to run `agenteye login` (it emails a one-time code and prompts
interactively — you can't complete it for them, and don't fabricate a token).
If a key was supplied, the key itself was rejected — say so and stop; logging in
is not the fix, and silently switching to a session would run the command as a
different identity than the user asked for.
- **base-url** → the CLI defaults to the hosted product,
`https://app.befailproof.ai`, so a plain `agenteye login` works out of the box.
Only pass `--base-url <url>` (or set `AGENTEYE_DASHBOARD_URL`) for a self-hosted
Expand All @@ -76,12 +102,28 @@ Before real work, run `agenteye --json whoami` and react to the exit code:
- **exit 0** → `whoami` returns the active org slug and your permissions; trust
that for the org name and to know what you're allowed to do before attempting a
gated command (don't assume a particular org slug — read it from `whoami`).
- **In key mode, `whoami` answers a different question.** It still exits 0 —
`whoami` never errors — but it reports *how* you are authenticated rather than
*who* you are: there is no signed-in user, so it says so and names the auth mode
and the org it will act on. Read the auth mode; don't read "no user" as "not
authenticated" and don't try to log in on the strength of it. Since it isn't a
permission check either, let your first real read (`agenteye --json list envs`)
be what confirms the key works.

**Multi-tenant:** a user can belong to several orgs; the active one is chosen at
login. Override for a single command with the global `--org <slug>`
(`agenteye --org acme sessions`); change the saved default with
`agenteye orgs switch <slug>`.

> ⚠️ **With a key, name the org explicitly.** A key bound to one organization only
> ever acts on that one. But a key that is **not** bound to a single organization
> has nothing to fall back on — key mode never reads a saved active org — so the
> deployment resolves it to its own default, and you get **that** org's data: no
> error, no warning, results that look perfectly valid. If you cannot tell which
> kind of key you hold, pass `--org <slug>` (or set `AGENTEYE_ORG`) on every
> command. Naming the org the key already belongs to is a no-op, and naming the
> wrong one fails loudly instead of quietly — both better than guessing.

## 4. Mutations: confirm with the user FIRST

The CLI normally prompts "are you sure?" before a destructive action — **but it
Expand Down Expand Up @@ -135,6 +177,9 @@ you need a flag you don't already know.
- `agent health|models|chats|ask|show|rename|delete` — built-in assistant; `agent ask "…"` starts a chat, `--chat <short-id>` continues one.

**Identity:** `login`, `logout`, `whoami`, `orgs {list,switch,current,perms}`, `version`, `help`.
All of `login` / `logout` / `orgs` — like the whole `agent` group — are **session-only**:
with a key they exit 2 without calling anything (§2). `keys update` needs a user too, but
fails as exit 5. `whoami`, `version` and `help` work either way.

## 6. Translating plain-English requests

Expand Down
50 changes: 37 additions & 13 deletions skills/agenteye-cli/references/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,32 @@ Set on the CLI, **before** the subcommand. Precedence: flag > env var > config f
| `--base-url <url>` | `AGENTEYE_DASHBOARD_URL` | Dashboard URL. Defaults to `https://app.befailproof.ai` (the hosted product); override for self-hosted/dev. Must start with `http://`/`https://`. |
| `--org <slug>` | `AGENTEYE_ORG` | Active org for this command (multi-tenant override). |
| `--token <t>` | `AGENTEYE_CLI_TOKEN` | Session token (normally from config after `login`). |
| `--api-key <k>` | `AGENTEYE_CLI_API_KEY` | Scoped API key — authenticate as a credential instead of as a signed-in user. Never saved to the config file. |
| `--json` | `AGENTEYE_CLI_JSON` | Machine-readable JSON to stdout, nothing else. |
| `--insecure` / `--secure` | `AGENTEYE_INSECURE` | Skip / require TLS verification (for self-signed dev certs; saved at login). |
| `--version` | | Print version (also `agenteye version`). |

Config dir honours `AGENTEYE_HOME`. Telemetry is currently disabled globally while its send path is made fully non-blocking. `AGENTEYE_ANALYTICS_DISABLED=1` and `DO_NOT_TRACK=1` remain supported opt-out controls for when telemetry is re-enabled.

### Session or API key
Exactly one credential is in play per invocation, chosen in this order:

| You supply | Result |
|---|---|
| `--api-key` **and** `--token` | usage error, exit 2 — it never guesses |
| `--api-key` | key mode |
| `--token` | session mode |
| `AGENTEYE_CLI_API_KEY` | key mode — **wins over `AGENTEYE_CLI_TOKEN`** |
| `AGENTEYE_CLI_TOKEN` | session mode |
| a saved session (from `login`) | session mode |
| nothing | exit 4 |

- **The key is never persisted.** A session token is saved by `login` and expires on its own; an API key is valid until someone revokes it, so the CLI keeps it out of the config file entirely. Supply it per invocation, normally via `AGENTEYE_CLI_API_KEY`.
- **`--api-key ""` means "no override", not "fall back".** Mode stays *key*, the credential is empty, and the command exits 4 — it will not quietly use a saved session. (Same rule as `--token ""`.) An empty *environment variable* is a different story: `AGENTEYE_CLI_API_KEY=""` reads as unset and falls through to the next credential, so an unset CI variable can silently run as whichever human is logged in on that machine. Pass the flag if you need the strict behaviour.
- **A rejected key is exit 4**, same as an expired session. Report it; don't retry and don't switch credentials.
- **Session-only commands:** `login`, `logout`, `orgs *`, `agent *`. In key mode each exits **2** *before making any request* — there's no user to sign in or switch orgs for, and no private assistant thread to own. `keys update` also requires a signed-in user, but it fails later and differently: the request goes out and comes back **exit 5**, because re-scoping keys needs a permission that cannot be granted to a key.
- **Name the org when using a key.** Key mode sends the org only when you supply it (`--org <slug>` / `AGENTEYE_ORG`) — it never reuses the saved active org from `login`. A key bound to one organization only ever acts on that one; a key that is not bound to a single organization falls back to the deployment's default, and you get plausible-looking data from the wrong tenant with no error and no warning. Naming the key's own org is a no-op; naming a different one is rejected. Both beat guessing.

## Shared input conventions
- **`--json`** on any command → pure JSON on stdout (no Rich chrome). Mutations under `--json` auto-skip their confirm prompt.
- **`--yes` / `-y`** explicitly skips a confirm prompt. (Confirms are also auto-skipped on a non-TTY — i.e. whenever Claude runs it — so always confirm with the user yourself first.)
Expand All @@ -44,11 +64,13 @@ Config dir honours `AGENTEYE_HOME`. Telemetry is currently disabled globally whi
## Identity

### login / logout / whoami
- `agenteye login [--email you@x.com] [--org <slug>]` — emails a one-time code; on a real TTY it's a single interactive box, else a plain prompt. Saves the session to `~/.agenteye/cli.json`. **You cannot complete this for the user** (it needs the emailed code). `--org` picks the tenant at login.
- `agenteye logout` — clears the saved session.
- `agenteye whoami` — active org + your permissions. Run this first; exit 4 = login needed.
- `agenteye login [--email you@x.com] [--org <slug>]` — emails a one-time code; on a real TTY it's a single interactive box, else a plain prompt. Saves the session to `~/.agenteye/cli.json`. **You cannot complete this for the user** (it needs the emailed code). `--org` picks the tenant at login. **Session-only** — exit 2 under a key.
- `agenteye logout` — clears the saved session. **Session-only** — exit 2 under a key (a key cannot be "logged out"; revoke it instead).
- `agenteye whoami` — active org + your permissions. Run this first; exit 4 = no usable credential.
**Under a key it answers a different question and still exits 0:** it reports that there is no signed-in user, names the auth mode, and gives the org it will act on. So branch on the auth mode, not on the absence of a user identity — and note that it does not prove the key is accepted or check any permission. Let the first real read do that.

### orgs
**Session-only, the whole group** — each exits 2 under a key, with no request made. Use `--org <slug>` per command instead.
- `orgs list` — your orgs + role in each (active marked).
- `orgs switch [<slug>]` — change the saved active org; omit slug to pick from a list (TTY only). **State change** (mild) — affects later commands.
- `orgs current` — identity card for the active org.
Expand All @@ -59,25 +81,25 @@ All read-only; never need confirmation.

### events
`agenteye events [filters] [--all]` — event log, newest first. **Default is the light,
payload-free feed** (`/api/events/summary`): rows carry `summary, is_error, error_type,
output_tokens, context_window, context_fill` (a server-computed `summary`, no raw payload).
payload-free feed**: rows carry `summary, is_error, error_type, output_tokens,
context_window, context_fill` (a server-computed `summary`, no raw payload).
`--session-id`, `--all`, and structured filters stay on this fast path. `--search` is the
exception: responses remain payload-free, but the server must scan `payload` to match the
free-text term, so broad searches can still be expensive. To get the raw `payload`, opt
into the **full feed** with `--full` (or `--fields payload`) — this hits the heavy
`/api/events`, which is slow at scale, so keep it bounded (pair `--full` with one
`--session-id`). e.g. `agenteye --json events --full --session-id run-1 --all | jq '.events[].payload'`.
into the **full feed** with `--full` (or `--fields payload`) — that read is slow at scale,
so keep it bounded (pair `--full` with one `--session-id`).
e.g. `agenteye --json events --full --session-id run-1 --all | jq '.events[].payload'`.
Filters: `--session-id <id>` `--agent-id <id>` `--event-type <csv>` `--env <csv>` `--since <window>` / `--from`/`--to` `--search <term>` (repeatable, payload OR-match).

#### Getting the raw payload
The default `events`/`errors` reads are payload-free. Only `--full` (or `--fields payload`)
returns the raw `payload`, and it hits the heavy `/api/events` feed — **always bound it**
(pair with `--session-id`); an unbounded `events --full` can time out / degrade ClickHouse at
returns the raw `payload`, and that is the heavy feed — **always bound it** (pair with
`--session-id`); an unbounded `events --full` can time out or degrade the event store at
scale.
- **A whole session:** `agenteye --json events --full --session-id <SESSION_ID> --all --limit 1000 | jq '.events[].payload'`
- **A single event:** scope to its session, then pick by id — `agenteye --json events --full --session-id <SESSION_ID> --all | jq '.events[] | select(.id == <EVENT_ID>) | .payload'`
- **An error's payload:** two steps — `agenteye --json errors --error-type <T> --since 24h` (gives the error's `id` and `session_id`; `errors` is light-only, no payload), then `agenteye --json events --full --session-id <SESSION_ID> --all | jq '.events[] | select(.id == <ERROR_EVENT_ID>) | .payload'`
- **Precise / by id (avoids the heavy list query):** `agenteye --json query run --sql "SELECT id, event_type, payload FROM events WHERE session_id = '<SESSION_ID>' ORDER BY ts"` — or `WHERE id = <EVENT_ID>`. Reads `payload` directly via the read-only SQL runner (`/api/queries/run`); a bounded `WHERE` is fast.
- **Precise / by id (avoids the heavy list query):** `agenteye --json query run --sql "SELECT id, event_type, payload FROM events WHERE session_id = '<SESSION_ID>' ORDER BY ts"` — or `WHERE id = <EVENT_ID>`. Reads `payload` directly via the read-only SQL runner; a bounded `WHERE` is fast.

### sessions
`agenteye sessions [filters] [--all]` — agent runs: time/env/agent/session/status (no scores). Filters: `--session-id --agent-id --env --status <error|...> --since`. JSON rows still carry `scores`.
Expand All @@ -87,7 +109,7 @@ scale.
`agenteye evals --aggregate [--since 7d]` — rollup: `{total, status_counts, score_stats, timeline}` (status mix + per-metric score stats). `--score helpfulness:..0.5` = max 0.5; `helpfulness:0.8..` = min 0.8; `helpfulness:0.5..0.9` = range.

### errors
`agenteye errors [filters] [--all]` — errored events (time/event/env/agent/session/summary), from the light payload-free feed (`/api/events/summary`); the `summary` is the server-computed field, and `--json` rows carry no payload. For a run's raw payload use `agenteye events --full --session-id <id>`. Filters incl. `--error-type <csv>`.
`agenteye errors [filters] [--all]` — errored events (time/event/env/agent/session/summary), from the light payload-free feed; the `summary` is the server-computed field, and `--json` rows carry no payload. For a run's raw payload use `agenteye events --full --session-id <id>`. Filters incl. `--error-type <csv>`.
`agenteye errors --aggregate [--since 7d]` — `{total, sessions, agents, last_ts, bins}`.

### usage
Expand All @@ -104,7 +126,7 @@ API keys; the secret is shown **once** on create/regenerate (capture it then). R
- `keys list [--show-id] [--fields ...]` — active keys first, then revoked.
- `keys show <name>`
- `keys create <name> [--permission-set <set>] [--add <tok>] [--remove <tok>]` — permissions work **exactly like `users create`**: optionally seed from a role with `--permission-set` (`read-only`/`standard`/`admin` or a custom org set), then fine-tune with `--add`/`--remove`. Effective grants = `(set ∪ added) − removed`. For a narrowly-scoped key (the common case) just use `--add` with no set: `keys create ci-pipeline --add events:add`. Secret → stdout when piped. (There is **no** positional `PERMISSIONS` arg and **no** `-p` flag — those forms error.)
- `keys update <name> [--permission-set <set>] [--add <tok>] [--remove <tok>]` — incremental on the key's CURRENT grants (merges --add/--remove), unless `--permission-set` is given (which reseeds, then applies --add/--remove). `--yes`/`-y` to skip confirm.
- `keys update <name> [--permission-set <set>] [--add <tok>] [--remove <tok>]` — incremental on the key's CURRENT grants (merges --add/--remove), unless `--permission-set` is given (which reseeds, then applies --add/--remove). `--yes`/`-y` to skip confirm. **Needs a signed-in user** — under a key it reaches the server and returns **exit 5**, because `keys:update` is never assignable to a key. Every other `keys` subcommand works under a key that holds the matching grant.
- `keys disable <name>` — revoke.
- `keys regenerate <name>` — rotate secret (old one dies).

Expand Down Expand Up @@ -179,6 +201,8 @@ Saved ClickHouse SQL + ad-hoc runner. Saved queries referenced by **name**.
- `query schema [TABLE]` — column layout; JSON `{schema, columns:[{table,column,type,nullable}]}`.

## agent
**Session-only, the whole group** — each subcommand exits 2 under a key, with no request made: a chat is private to the person who owns it, and a key is not a person.

Built-in assistant. Chats referenced by a **short chat-id** (first 8 hex; prefix-resolved).
- `agent health` · `agent models` (available models for `--model`, default marked).
- `agent chats` — `chat-id · title · messages · updated`.
Expand Down