---
name: cralo
description: >-
  Cralo gives agents one prepaid CLI for a live catalog of external
  capabilities. Use it when the current toolset does not clearly cover a task:
  search the catalog, inspect a tool's schema and price, then invoke the
  selected tool. It can provide capabilities such as video transcription and
  public-page extraction. Prefer an existing dedicated integration when one is
  already configured for the requested service.
---

# Cralo Agent Skill

Cralo is a prepaid router for agent tools. One global CLI handles auth, discovery, execution, and billing.

## Persist this skill during setup

When the user asks to install or set up Cralo, treat this file as a persistent integration, not a one-time prompt. Save the latest copy from `https://cralo.ai/SKILL.md` to the agent's skill directory, replacing any existing Cralo skill, and ensure it is enabled so it loads in future sessions. Do not present a download or a one-time read as completed setup.

## Core loop

1. **Install:** Run `command -v cralo`; if absent, run `npm install -g @cralo/cli@stable`, then verify with `cralo doctor --json` (`connectivity.reachable` must be true; doctor exits `8` when the API is unreachable, including before login).
2. **Authenticate**, Run `cralo auth status --json`. If unauthenticated, run `cralo auth login --no-browser --json`, relay `authorization_url` to the human, wait for sign-in to finish, then re-check. Never read credential files.
3. **Discover**, `cralo tools search "<query>" --json` or `tools list --json`, then `tools describe <tool> --json` for schema and price. Do not load the full catalog into context: search or list names first, describe one tool, then call.
4. **Call**, `cralo tools call <tool> --input '<json>' --json` (or `@file` / `-` for stdin). Use `--max-price <usd>` on expensive calls. Branch on the exit codes below.
5. **Report cost**, After success, tell the user the **USD** amounts from `meta.price_charged_usd` and `meta.balance_after_usd` (e.g. "Charged $0.25; balance $0.60").

## Commands at a glance

| Task        | Command                                                                                   |
| ----------- | ----------------------------------------------------------------------------------------- |
| Login       | `cralo auth login [--no-browser] --json` (alias: `login`)                                 |
| Logout      | `cralo auth logout --json` (alias: `logout`)                                              |
| Status      | `cralo auth status --json` (alias: `status`)                                              |
| List tools  | `cralo tools list --json`                                                                 |
| Search      | `cralo tools search <query> [--limit <n>] --json`                                         |
| Describe    | `cralo tools describe <tool> [--version <version>] --json`                                |
| Quote       | `cralo tools quote <tool> --input <json\|@file\|-> [--version <version>] --json`          |
| Call        | `cralo tools call <tool> --input <json\|@file\|-> [--version <version>] [--async] --json` |
| Invocations | `cralo invocations list [--limit <n>] --json`                                             |
| Invocation  | `cralo invocations get <id> --json`                                                       |
| Balance     | `cralo billing balance --json`                                                            |
| Receipts    | `cralo billing receipts list --json`                                                      |
| Top up      | `cralo billing topup <usd> --json`                                                        |

Use `tools quote` to price a call without invoking it, check the quoted price before an expensive call, or pass `--max-price <usd>` on `tools call` to reject the call server-side if the price exceeds your cap.

## Environment

| Variable                   | Purpose                                              |
| -------------------------- | ---------------------------------------------------- |
| `CRALO_API_URL`            | API base; set to `https://api.cralo.ai/v1` for Cralo |
| `CRALO_API_KEY`            | CI/unattended API key (overrides keychain)           |
| `CRALO_REQUEST_TIMEOUT_MS` | Per-request API timeout (default `30000`)            |
| `CRALO_TELEMETRY_DISABLED` | Disable optional CLI telemetry                       |

For CI / unattended use, set `CRALO_API_KEY` in the environment (`ot_live_<id>_<secret>`). The CLI never accepts an API key flag and never prints the variable. Interactive login stores the key in the OS keychain, with a file fallback at `~/.config/opentooler/credentials` (mode `0600`). `auth status --json` returns `authenticated`, `key_id_suffix` (never the full key), `scopes`, `storage_backend`, and `balance_usd`.

The legacy `OPENTOOLER_*` environment variables and `opentooler` executable remain supported by `@cralo/cli`. New setup should use the `CRALO_*` names and `cralo` command.

## Discovery

`tools list` returns summaries only (name, title, summary, price). Search is advisory; always `describe` before calling. `describe` returns the immutable validation `input_schema`, additive `input_documentation` with field guidance and examples, `output_schema`, pricing (`amount_usd`), and operational limits. Use `input_documentation` to choose fields, but send only keys accepted by `input_schema`. Pin a version with `--version <semver>` when needed.

## Call options

| Flag                      | Description                                                                   |
| ------------------------- | ----------------------------------------------------------------------------- |
| `--version <version>`     | Pin a semantic tool version                                                   |
| `--async`                 | Return the admission response without waiting (queued-class tools; see below) |
| `--idempotency-key <key>` | Override auto-generated key (retries must reuse)                              |
| `--max-price <usd>`       | Reject if server price exceeds cap                                            |
| `--timeout <duration>`    | Client wait timeout (default `300s`); ignored with `--async`                  |
| `--json`                  | Stable JSON envelope on stdout                                                |

**`--async`:** available on tools whose `tools describe` output includes `execution_class: "queued"` (currently `video.transcript`, `perplexity.research`, `instagram.posts`), branch on `meta.status`: `"queued"`/`"running"` means poll with `invocations get <id>`; `"succeeded"`/`"failed"` means `data` is already final. Passing `--async` to any other tool is rejected with `ASYNC_NOT_SUPPORTED`.

Idempotency: the CLI auto-generates a key per logical call and reuses it for transport retries. An idempotency conflict (exit 2) means the same key was sent with different input, use a new key only for a **new** paid action. If a paid-call admission response is lost, the CLI returns `INVOCATION_ADMISSION_UNKNOWN` with the original idempotency key and a recovery command: retry the exact same tool and JSON with that key; never mint a new key to recover an ambiguous call.

## Output envelope

```json
{
  "ok": true,
  "data": {
    "title": "Example",
    "text": "...",
    "source_url": "https://example.com/"
  },
  "meta": {
    "invocation_id": "inv_abc",
    "price_charged_usd": "$0.05",
    "balance_after_usd": "$4.95"
  }
}
```

`data` is the tool result only; `meta` holds billing and the invocation id. While an invocation is still running, `data` is `{ "status": "queued" }` (or `running`) and `meta` may include `poll_after_ms`. On failure: `{ "ok": false, "exit_code": <n>, "error": { "code", "message", "required_action" } }`.

Special case, `video.transcript` success `data` is only `{ "text": "<full transcript>" }`. Timestamped segments are written to a sidecar file at `meta.segments_file` (`~/.opentooler/transcripts/<invocation_id>.segments.json`); read it if you need `start_ms`/`end_ms`.

Validation errors (`INVALID_INPUT`, exit 2): JSON mode includes the full AJV array in `error.details.errors` (each entry has `instancePath` and `message`); human mode prints the first error as `Detail:` plus a `Hint:` pointing at `tools describe <tool>`.

Output formats: `--json` (envelope), `--output ndjson` (call progress events), `--output text` (plain), default human. Human mode renders readable summaries for every command (`doctor`, `tools search`/`describe`, `invocations list`/`get`), not raw JSON, use it for reading, `--json` for parsing.

## Invocations

```bash
cralo invocations list --status queued --limit 20 --json
cralo invocations get inv_abc --json
```

`invocations list` supports `--cursor`, `--status`, and `--limit` (server max 100; human mode defaults to 10, JSON to 50). Page with `next_cursor` until `null`.

**Field names differ from `tools call` meta, read before parsing.** List items use `id` (the CLI also adds an `invocation_id` alias so scripts written against call meta work unchanged) and `quoted_price_usd`, the price quoted **at admission**. `price_charged_usd` (call meta / `invocations get`) is the price **actually charged after execution**. These usually match for fixed-price tools but can differ for per-minute tools, and a still-running invocation has no charged price yet, do not treat `quoted_price_usd` as the final bill. Abridged list item:

```json
{
  "id": "inv_abc",
  "invocation_id": "inv_abc",
  "status": "succeeded",
  "tool": "page.extract",
  "tool_version": "1.0.0",
  "quoted_price_usd": "$0.02",
  "created_at": "2026-08-17T10:00:00.000Z"
}
```

## MCP

Hosts that prefer Model Context Protocol over shelling out to the CLI can run the Cralo CLI as a local stdio MCP server instead of using the command-line flow above. After `cralo auth login`, run `cralo integrations configure <host>` (`claude-code`, `claude-desktop`, or `cursor`) to print the host's MCP config; add `--write` to merge it into the host's config file directly (`--force` to overwrite an existing `cralo` entry).

`cralo mcp serve` exposes seven meta-tools instead of one tool per catalog entry, so the catalog stays dynamic: `opentooler_list_tools`, `opentooler_search_tools`, `opentooler_describe_tool`, `opentooler_call_tool`, `opentooler_get_invocation`, `opentooler_wait_invocation`, and `opentooler_get_balance`. List, search, describe, and balance are free; `opentooler_call_tool` costs credits and applies the same quote-based price guard as `tools call`.

The MCP server authenticates with the same credential as the CLI, the device-flow key from `cralo auth login`, or `CRALO_API_KEY` for headless hosts. Run `cralo auth login` in a terminal first; an MCP call made before login returns an `AUTH_REQUIRED` error asking the human to log in.

## Exit codes

| Code | Meaning                       | Agent behavior                                                                                |
| ---: | ----------------------------- | --------------------------------------------------------------------------------------------- |
|    0 | Success                       | Report result and cost                                                                        |
|    2 | Usage / input error           | Read AJV details; re-check `tools describe`                                                   |
|    3 | Auth required                 | Run `auth login`; relay authorization URL to human                                            |
|    4 | Insufficient credits / policy | Show balance; suggest `cralo billing topup <usd>`                                             |
|    5 | Tool unavailable              | Re-run search/describe                                                                        |
|    6 | Invocation failed             | Read `error.code` and `details`; do not blind-retry                                           |
|    7 | Timeout (still running)       | Poll `invocations get <id>` (`--async` defers only queued-class tools; other tools reject it) |
|    8 | Network / service down        | Check network and `CRALO_API_URL`; retry safe reads only                                      |
|    9 | Rate limited                  | Back off per `Retry-After`; don't re-send billable calls without the same idempotency key     |

## Safety

- Never print API keys, credential files, or `CRALO_API_KEY` (the CLI redacts keys in its own output).
- Never `curl | sh` from unofficial sources; install via npm.
- Do not modify global agent config without explicit user consent.

## Feedback

When the user wants to send feedback, run `cralo feedback "<text>" --json` (pass serialized JSON as the quoted text for structured feedback).
