> For the complete documentation index, see [llms.txt](https://docs.drizz.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.drizz.dev/twins/reference/cli.md).

# CLI reference

Every twin command for signing in, making worlds, running agents in them and reading what happened, with their most useful options.

Every `twin` command, with its most useful options.

The `twin` command comes with the `twin-harness` package. This page covers version 0.11.1. In a terminal, `twin help <command>` or `twin <command> --help` prints any command's help.

```bash
twin --version
twin help world create        # one command's help
twin help environment         # every environment variable
twin help exit-codes          # what each exit status means
```

## At a glance

```bash
twin login                                         # sign in (your browser opens)
twin --list                                        # the twins you can use
twin world create seed.md --folder world           # a world with your data
twin world run world -- python my_agent.py "<task>"  # your agent works in it, over MCP
twin world calls world --json > traces.json        # every call the twin recorded
twin world delete world                            # done with it
```

## Sign in

| Command       | What it does                                                                          |
| ------------- | ------------------------------------------------------------------------------------- |
| `twin login`  | sign in with your browser. An API key is minted and kept for this machine             |
| `twin whoami` | the key kept here, who it belongs to, where it is kept, and whether the twin takes it |
| `twin logout` | revoke the key kept here on the server, and forget it                                 |

**`twin login`**

| Option            | What it does                                                                                   |
| ----------------- | ---------------------------------------------------------------------------------------------- |
| `--server SERVER` | the twin server (default: `TWIN_SERVER`, else Drizz's hosted twins)                            |
| `--name NAME`     | what to call the key (default: `twin-harness <this machine>`)                                  |
| `--no-browser`    | print the sign-in link only                                                                    |
| `--with-key -`    | keep a key minted elsewhere, read from stdin. For CI: `echo "$KEY" \| twin login --with-key -` |
| `--json`          | print JSON; the sign-in's progress goes to stderr                                              |

**`twin whoami`**

| Option            | What it does                                                                     |
| ----------------- | -------------------------------------------------------------------------------- |
| `--server SERVER` | the twin server whose key to show (default: the one kept, else the hosted twins) |
| `--json`          | print JSON                                                                       |

Exit status: 0 the twin takes the key, 4 not signed in or the key is refused, 69 the twin did not answer.

**`twin logout`**

| Option            | What it does                                                                        |
| ----------------- | ----------------------------------------------------------------------------------- |
| `--server SERVER` | the twin server to sign out of (default: the one kept)                              |
| `--local`         | forget the key here without revoking it. It still works on the server until revoked |
| `--everywhere`    | also end every sign-in session you hold                                             |
| `--no-browser`    | if a sign-in is needed first: print the link and code only                          |
| `--json`          | print JSON                                                                          |

With no live sign-in session, `twin logout` opens your browser to sign in first, so it can revoke the key. Exit status: 0 revoked and forgotten (or nothing to do), 2 the server refused or the sign-in did not finish, 69 the server did not answer. In both failure cases the key is kept.

See [Keys and limits](/twins/reference/keys-and-limits.md).

## Twins

| Command                     | What it does                                                             |
| --------------------------- | ------------------------------------------------------------------------ |
| `twin --list`, `twin twins` | the twins you can use, what each is, and the World section that seeds it |
| `twin start <twin>`         | a world to try a twin with, and the lines that connect an agent to it    |

**`twin twins`**

| Option   | What it does |
| -------- | ------------ |
| `--json` | print JSON   |

**`twin start <twin>`**

| Argument or option | What it does                                                                                     |
| ------------------ | ------------------------------------------------------------------------------------------------ |
| `<twin>`           | which twin: `calendar`, `gmail`, ... (`twin --list` lists them)                                  |
| `--lease LEASE`    | seconds it lives, when you are signed in (default 1 hour). A trial world's lease is the server's |
| `--server SERVER`  | the twin server, for a trial world (default: `TWIN_BASE`'s, else Drizz's hosted twins)           |
| `--label LABEL`    | up to 12 letters, digits or hyphens, to list and clean up the world by (default: `start`)        |
| `--json`           | print JSON                                                                                       |

```bash
twin start calendar                      # a world and the line that connects your agent to it
twin start gmail --lease 3600 --json
twin world cleanup --label start --yes   # delete every world twin start made
```

## Worlds

A world is a private copy of the twins, seeded from a file and kept in a folder. Every `twin world` command after `create` takes that folder.

| Command                                | What it does                                                          |
| -------------------------------------- | --------------------------------------------------------------------- |
| `twin world create <file>`             | a fresh world, seeded from a seed file or a snapshot                  |
| `twin world run <folder> -- <command>` | run an agent in the world                                             |
| `twin world calls <folder>`            | the twin's record of every call made in the world                     |
| `twin world trigger <folder> <file>`   | make things happen in the world now: new mail arriving, new events    |
| `twin api <folder> <tool>`             | call one of the world's MCP tools and print what it returns           |
| `twin world save <folder>`             | the world as it stands now, as a seed file `create` makes again       |
| `twin world snapshot <folder>`         | the world's exact state, every app, as JSON `create` makes again      |
| `twin world handle <folder>`           | the world as JSON, for `attach` on another machine (holds its tokens) |
| `twin world attach <handle>`           | pick up a world from a `handle` file, as a local folder               |
| `twin world list`                      | the worlds this machine made that still exist                         |
| `twin world cleanup`                   | delete the worlds of a label                                          |
| `twin world delete <folder>`           | delete the world and its folder (or a world by its id)                |

### twin world create

```bash
twin world create seed.md --folder world              # calendar and mail, as seed.md describes
twin world create seed.md --folder run1 --label demo  # a label lists and cleans up worlds together
twin world create snap.json --folder again            # exactly the world a snapshot kept
```

| Option            | What it does                                                               |
| ----------------- | -------------------------------------------------------------------------- |
| `--folder FOLDER` | where to keep the world's handle (default: a new temp folder)              |
| `--lease LEASE`   | seconds the world lives if never deleted (default 6 hours)                 |
| `--label LABEL`   | up to 12 letters, digits or hyphens, to list and clean up worlds by        |
| `--person PERSON` | whose calendar and mailbox (default: your first identity)                  |
| `--json`          | print JSON: the folder, the world ids and each app's MCP address and token |

The folder keeps the world's address and tokens in `handle.json`, private to you.

### twin world run

```bash
twin world run world -- python my_agent.py "Move my Dentist to Thursday 2pm"
```

Your agent starts with `TWIN_MCP`, `TWIN_TOKEN` (and `TWIN_GMAIL_MCP`, `TWIN_GMAIL_TOKEN` for a mailbox) and `TWIN_NOW` in its environment. Never your sign-in key. Exit status: your agent's own.

### twin world calls

```bash
twin world calls world --writes
twin world calls world --app gmail --status error
twin world calls world --follow --json | jq .
```

| Option                        | What it does                                                                                                              |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `--follow`, `-f`              | keep reading: print each new call as the twin records it, until Ctrl-C                                                    |
| `--interval INTERVAL`         | with `--follow`: seconds between reads (default 1)                                                                        |
| `--app {calendar,gmail}`      | only this app's calls                                                                                                     |
| `--writes`                    | only the calls that changed something                                                                                     |
| `--status {ok,error,4xx,5xx}` | only calls that ended so: `ok`, `error` (any failure), `4xx` (the caller's: denied, invalid, unknown), `5xx` (the twin's) |
| `--json`                      | print JSON. With `--follow`, one object per line                                                                          |

### twin world trigger

```bash
twin world trigger world new-mail.md
```

Adds the mail and events a file describes (`## World: gmail` and `## World` lines) to the world as it stands. `now` in place of a mail line's day and time dates it by the twin's clock. `--json` prints what landed.

### twin api

```bash
twin api world --list
twin api world events.list -f calendarId=primary -F maxResults=5
twin api world message.list --app gmail -f q="is:unread"
twin api world events.create --input event.json
```

| Option                        | What it does                                                                         |
| ----------------------------- | ------------------------------------------------------------------------------------ |
| `-f`, `--raw-field key=value` | an argument, as a string                                                             |
| `-F`, `--field key=value`     | an argument, read as JSON when it is JSON (numbers, `true`, objects)                 |
| `--input file`                | the arguments as a JSON object, from a file (`-` for stdin). `-f` and `-F` add to it |
| `--app {calendar,gmail}`      | the server to call (default: the one that has the tool)                              |
| `--list`                      | list the tools instead of calling one                                                |
| `--json`                      | the output is JSON either way                                                        |

A calendar tool whose schema needs a `calendarId` gets `primary` when you give none. The call is in the world's record of calls, as your agent's would be. Exit status 2 when the twin answers with an error.

### twin world save and snapshot

```bash
twin world save world                  # print it
twin world save world -o after.md      # keep it
twin world snapshot world -o snap.json # an exact copy
```

| Option            | `save`                                                  | `snapshot`                           |
| ----------------- | ------------------------------------------------------- | ------------------------------------ |
| `-o`, `--out OUT` | the file to write (default: print it)                   | the `.json` file to write (required) |
| `--force`         | replace the file if it exists                           | replace the file if it exists        |
| `--json`          | print JSON. The seed file is in it unless `-o` is given | print JSON                           |

### twin world handle and attach

```bash
twin world handle world > world.json   # then, on another machine or CI step:
twin world attach world.json           # a local folder the other commands take
```

`--json` on `attach` prints `{"folder": ...}`. The handle holds the world's tokens: treat it as a secret.

### twin world list

| Option          | What it does                         |
| --------------- | ------------------------------------ |
| `--label LABEL` | only the worlds made with this label |
| `--json`        | print JSON                           |

Shows this machine's worlds that still exist, and how full your organization is. A world made from a seed file with mail is two worlds, a calendar and a mailbox, in one folder.

### twin world cleanup

| Option                    | What it does                                                                                                                   |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `--label LABEL`           | the worlds made with this label                                                                                                |
| `--all`                   | every world made on this machine, and every older `t-...` world your key can list. A teammate's newer worlds are never touched |
| `--older-than OLDER_THAN` | only those made more than this many seconds ago                                                                                |
| `--dry-run`               | list what would be deleted, and delete nothing                                                                                 |
| `--yes`, `-y`             | delete without asking (needed where there is no terminal: CI, a script)                                                        |
| `--json`                  | print JSON                                                                                                                     |

### twin world delete

```bash
twin world delete world          # the world on the twin, and its folder
twin world delete 3f9c2a7b1e04   # a world by its id (twin world list), such as one twin start made
```

A world the twin did not delete keeps its folder: run `delete` again.

## Help and shell

| Command                           | What it does                                                                                          |
| --------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `twin help [topic]`               | help on a command (`world`, `world create`, ...) or a topic: `environment`, `exit-codes`, `reference` |
| `twin completion {bash,zsh,fish}` | print a shell completion script                                                                       |
| `twin --version`, `-V`            | the version                                                                                           |

```bash
echo 'eval "$(twin completion zsh)"' >> ~/.zshrc     # zsh, after compinit
echo 'eval "$(twin completion bash)"' >> ~/.bashrc   # bash
twin completion fish > ~/.config/fish/completions/twin.fish
```

Homebrew installs completion for you.

## The example agent

`twin-agent` is an example agent that ships with the package: a model with the twin's tools. Run it in a world:

```bash
twin world run world -- twin-agent "Move my Dentist to Thursday 2pm"
```

It reads its model from `.env` in the current folder: `LLM_BASE` (an OpenAI-compatible endpoint), `LLM_MODEL` and `LLM_KEY`. `PROVIDER=<name>` reads `.env.<name>` instead.

## Exit codes

The same for every `twin` command, so a script or CI step can tell them apart.

| Status | Meaning                                                                                                                                     |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`    | it worked                                                                                                                                   |
| `2`    | usage or setup error: a wrong option, a missing file or folder. The message says which. Also `twin api` when the twin answers with an error |
| `4`    | sign-in needed: not signed in, or the twin refused the key. Run `twin login`                                                                |
| `69`   | a Drizz service is down. Not your agent, not your setup: try again later                                                                    |
| `130`  | stopped with Ctrl-C. The worlds the command made were deleted on the way out                                                                |
| `143`  | stopped by SIGTERM (a cancelled CI job), cleaned up the same way                                                                            |

`twin world run` ends with your agent's own exit status.

With `--json`, a command prints an error as JSON on stderr:

```json
{"error": {"code": 4, "message": "not signed in -- run `twin login`"}}
```

## Environment variables

The ones you are most likely to set. `twin help environment` lists them all.

| Variable                  | What it does                                                                                                               |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `TWIN_SERVER`             | the twin server `twin login` signs in to, and its key is for (default: Drizz's hosted twins)                               |
| `TWINS_API_KEY`           | a key minted elsewhere, used instead of a kept one                                                                         |
| `TWIN_CREDENTIAL_STORE`   | where `twin login` keeps the key: `keychain`, `secret-service`, `file` or `auto` (default)                                 |
| `TWIN_CREDENTIALS`        | the file to keep it in, instead of `~/.config/twin/credentials.json` (implies `file`)                                      |
| `TWIN_FAULTS`             | faults injected into your agent's calendar calls: `light`, `medium`, `nonatomic`, `quiet`, or fault lines, comma-separated |
| `TWIN_WORLD_SHARED`       | `1`: worlds your organization shares, instead of private ones                                                              |
| `TWIN_SKIP_HEALTH`        | `1`: skip asking the twin's `/health` before a command starts                                                              |
| `TWIN_DEBUG`              | `1`: an error comes with its traceback                                                                                     |
| `TWIN_LOG_LEVEL`          | the log messages shown on stderr: `WARNING` (default), `INFO`, `ERROR`                                                     |
| `TWIN_NO_UPDATE_NOTIFIER` | `1`: never check for a newer release (checked once a day, at a terminal, outside CI)                                       |

Set for your agent by `twin world run` (read them; don't set them):

| Variable           | What it is                                        |
| ------------------ | ------------------------------------------------- |
| `TWIN_MCP`         | the calendar's MCP address in this world          |
| `TWIN_TOKEN`       | your agent's token for it                         |
| `TWIN_GMAIL_MCP`   | the mailbox's MCP address, when the world has one |
| `TWIN_GMAIL_TOKEN` | your agent's token for it                         |
| `TWIN_NOW`         | the moment the world is pinned at                 |

## Next

* [Keys and limits](/twins/reference/keys-and-limits.md): signing in, keys in CI, and how many worlds you can hold.
* [Troubleshooting](/twins/reference/troubleshooting.md): what an error or exit status means, and what to do.
* [Glossary](/twins/reference/glossary.md): the words used across these docs.

***

*Last updated: 27 October 2026*
