> ## Documentation Index
> Fetch the complete documentation index at: https://docs.engini.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Machine contract

> Exit codes, output modes, introspection, result handles, and resumable gates - the CLI's contract for scripts and agents.

The `engini` CLI is built to be driven programmatically. Everything on this page is stable across the npm and pip distributions.

## Exit codes

| Code | Meaning                                                                  |
| ---- | ------------------------------------------------------------------------ |
| 0    | OK                                                                       |
| 1    | Generic failure (failed liveness check, failed refresh, OAuth denied...) |
| 2    | Usage error (missing/invalid arguments)                                  |
| 3    | Authentication required or failed                                        |
| 4    | Not found                                                                |
| 5    | Validation - **including `need:*` gates that tell you the next step**    |
| 124  | Timeout (a polled async job didn't finish)                               |

## Output modes

* **TTY**: human-readable output; progress lines go to stderr.
* **Piped or `--json`**: pretty JSON on stdout - always machine-parseable.
* **`--quiet`**: suppresses non-essential output.
* Errors are structured too: a failure prints a JSON error object with a `hint` where applicable.

## Introspection: `--schema`

Every command supports `--schema`, printing its argument and output schema as JSON - agents discover flags without parsing help text:

```bash theme={null}
engini tools call --schema
```

## Resumable gates: `need:*` + exit 5

`engini connect` **never blocks** for a non-TTY caller. At each point where it needs input it prints a self-describing payload and exits `5`:

```bash theme={null}
# direct-credential (e.g. monday)
engini connect monday --json                  # → {need:"fields", resume:"engini connect monday --auth 0 --field ApiKey=<value>"}
engini connect monday --auth 0 --field ApiKey=<key>   # creates → checks → refreshes

# OAuth2 (e.g. outlook) — never opens a browser for the agent
engini connect outlook --json                 # → {need:"oauth", sign_in_url, state, resume:"engini connect outlook --oauth-state <state>"}
#   show sign_in_url to the user; after they sign in:
engini connect outlook --oauth-state <state>  # polls token → creates → checks → refreshes

# object selection (apps that support it) — resume never re-creates
engini connect <app> --json                   # → {need:"objects", connection_id, objects, resume:"engini connect <app> --connection <id> --object <id>"}
engini connect <app> --connection <id> --object 5 --object 9
```

Each gate payload contains everything needed to proceed (`methods`, `fields`, `sign_in_url`, `objects`...) plus a `resume` string with the **exact next command**. The loop is: run → read `need` → obtain the value (ask your user) → run `resume`.

## Result handles

Tool results larger than `--max-bytes` (default 4096) are spilled to a local sandbox and replaced by a compact envelope with a **handle** and a shape preview. Drill in instead of re-fetching:

```bash theme={null}
engini tools result h42 --select items.email      # dot-path (maps over a list)
engini tools result h42 --fields id,name           # project keys per item
engini tools result h42 --select items --offset 20 --limit 10   # page a list
engini tools result h42 --full                     # the whole payload
engini tools result --list                          # known handles
engini tools result --clear                         # wipe the sandbox
```

Stored results live under `$XDG_CACHE_HOME/engini/results/` (`%APPDATA%` on Windows) and are auto-pruned to the most-recent 20, dropping anything older than 24h - handles are short-lived, so drill in during the same session. `--inline` (or `--max-bytes 0`) forces the full result inline; `--raw` / `--llm` bypass the sandbox.

## LLM-shaped output

`engini tools call ... --llm openai|anthropic --tool-call-id <id>` emits the result already formatted as that vendor's tool-result message - pipe it straight back into the model conversation.

## Previews

Mutating commands accept `--dry-run`, printing what would happen (e.g. `{"would_connect": ...}`) without executing.
