# CLI

The `thomas` binary, installed by `npm install -g @openthomas/openthomas`.

## Commands at a glance

```
SETUP
  openthomas wire                    Detect agents, wire them, start the daemon.
  openthomas unwire                  Restore agent configs (leaves the daemon running).

INSPECT
  thomas                         Open the UI (default subcommand).
  openthomas status                  Daemon + tap health.
  openthomas list                    Recent runs.
  openthomas show <run-id>           Inspect one run.
  openthomas tail                    Live-tail actions across all agents.

EXPORT
  openthomas report <run-id>         Generate a shareable markdown/html/json report.
  thomas export                  Bulk-export traces.

CONTROL
  openthomas route                   Configure per-agent model routing & strategies.

REPLAY
  openthomas replay <run-id>         Mock-replay a run locally (OSS: mock only).

UPDATE
  openthomas update                  Update OpenThomas to the latest version.
  openthomas rollback                Revert to the version before the last update.

INTERNAL  (users don't call these directly)
  openthomas daemon                  Run the daemon in foreground (launchd target).
  thomas tap --agent X --server Y -- <cmd> [args…]
                                 stdio MCP wrapper.
```

The default subcommand when `thomas` is called with no args is `ui` (opens
the browser).

---

## `openthomas wire`

Detect installed agents, write proxy configuration, install the daemon
service, start the daemon.

```
USAGE
  openthomas wire [agents…] [options]

ARGUMENTS
  agents…          Wire just these agents (e.g. `claude-code`, `codex`).
                   Additive — agents not listed stay as they are.
                   Defaults to all detected.

OPTIONS
  --dry-run        Show what would change; do not write anything.
  --diff <agent>   Show the exact diff for an agent without applying.
  -y, --yes        Skip confirmation prompts.
  --include-projects
                   Also wire project-level configs (default: user-global only).
```

Output sketch:

```
$ openthomas wire
Putting OpenThomas on the wire…

Found 3 agents:

  ✓ claude-code  v1.0.123
      ~/.claude/settings.json
      • env.ANTHROPIC_BASE_URL → http://localhost:9877/wire/claude-code/anthropic
      • 4 MCP servers will be wrapped (github, filesystem, slack, gmail)

  ✓ openclaw     v0.8.2
      ~/.openclaw/openclaw.json
      • providers.anthropic.base_url → http://localhost:9877/wire/openclaw/anthropic
      • providers.openai.base_url    → http://localhost:9877/wire/openclaw/openai

  ⓘ cursor       v0.42.0
      ⚠ Cursor must be configured manually. See: openthomas wire --show cursor

Apply? [Y/n]
```

After write, if a long-running agent is detected (OpenClaw, Hermes), the
CLI also prompts for the apply step it requires (a daemon restart or a
paste-able `/model` command).

---

## `openthomas unwire`

Restore every config touched by `openthomas wire`. By default the daemon is
**left running** so historical traces, tasks, and the UI stay accessible.
Pass `--stop-daemon` to also tear down the service.

```
USAGE
  openthomas unwire [agents…] [options]

ARGUMENTS
  agents…          Unwire just these agents (others stay wired).
                   Defaults to all wired.

OPTIONS
  --force          Whole-file restore from the backup snapshot instead
                   of reverse-replaying the recorded edits. Discards any
                   changes the agent made to its config since wiring.
  --stop-daemon    Also stop and uninstall the daemon service. Without
                   this flag the daemon keeps running so you can still
                   browse what your fleet did before being unwired.
```

Behavior:

1. Read `~/.openthomas/backups/manifest.json` (latest entries).
2. For each entry, **reverse-replay** the edits OpenThomas recorded — undo only
   the pointers `openthomas wire` actually wrote, in reverse order, with the
   format-preserving editors.
3. Any pointer whose current value no longer matches what OpenThomas wrote was
   changed by the agent (or you) since wiring — it is left untouched and
   reported as "modified after wiring, left as-is".
4. Without `--stop-daemon` (default): leave the daemon running and
   **tombstone** the agent's routes — they stay in `routes.json` and
   the proxy keeps forwarding them, so already-running agent processes
   booted with the proxy URL keep working until they restart. But the
   model registry and Routing UI treat them as gone, so providers and
   models for the unwired agents disappear from the "switch to"
   dropdown. Secrets are kept so upstream auth still works for those
   live processes.
5. With `--stop-daemon`: hard-delete wire-authored state (routes,
   secrets, seeded registry rows, dead routing rules), then uninstall
   the launchd / systemd service. Already-running agent processes will
   fail to reach OpenThomas until restarted — at which point they read the
   restored config and go direct.
6. Archive the manifest to `~/.openthomas/backups/.history/`.

`--force` (and legacy v1 manifests, which predate recorded edits) fall back
to a byte-exact whole-file restore from `backupPath` — this overwrites any
changes the agent made to the file after wiring.

**Invariant**: after `openthomas unwire`, every edit `openthomas wire` made is
reversed; edits the agent made to its own config afterwards are preserved.

---

## `openthomas status`

```
$ openthomas status
OpenThomas
  Daemon:    running (pid 41234, started 12h 4m ago)
  Service:   com.openthomas.daemon (launchd)
  UI:        http://localhost:9877
  Storage:   ~/.openthomas/wire/traces (3 days, 142 MB)

Wired agents:
  claude-code   3 routes, 4 MCP wrappers
  openclaw      2 routes (last restart 4h ago)
  hermes        1 route   (running, pid 38912)

Recent activity (last hour):
  127 actions captured · $1.84 spent · 0 risk flags
```

---

## `openthomas list`

```
USAGE
  openthomas list [options]

OPTIONS
  --agent <id…>    Filter by agent.
  --since <when>   '1h' | '24h' | '7d' | ISO date.
  --status <s…>    'running' | 'done' | 'error'.
  --limit <n>      Default 20.
  --json           Output JSON for piping.
```

Default table columns: run_id, started_at, agent, status, duration, actions,
cost.

---

## `openthomas show <run-id>`

Full detail of a single run: action-by-action timeline, with input/output of
each model call (truncated by default; `--full` to expand), tool calls, MCP
messages, risk tags.

```
USAGE
  openthomas show <run-id> [options]

OPTIONS
  --full           Show full prompts/responses (no truncation).
  --json           Output normalized JSON.
  --raw            Output original wire-level req/res.
```

---

## `openthomas report <run-id>`

Generate a shareable report. The killer feature for "Send me the OpenThomas
trace."

```
USAGE
  openthomas report <run-id> [options]

OPTIONS
  --format <fmt>   'md' (default) | 'html' | 'json'
  --output <file>  Default: stdout
  --include-raw    Embed gzipped raw req/res (for offline replay)
  --redact         Replace tokens that look like secrets with [REDACTED]
```

Default `md` output is designed for pasting into GitHub issues, Discord,
Slack.

---

## `openthomas prune`

Delete captured traces older than a cutoff to reclaim disk and speed
up queries. The trace database can grow large after weeks of use
(typically a few hundred MB per active month).

```
USAGE
  openthomas prune [options]

OPTIONS
  --before <when>  '7d' | '30d' (default) | '90d' | '6m' | ISO date
  -y, --yes        Skip confirmation prompt.
  --json           Output JSON for piping.
```

By default the prune deletes rows but does not run `VACUUM` (which
takes ~1 minute and pauses writes). To reclaim disk to the OS, set
`THOMAS_PRUNE_VACUUM=1` in the daemon environment before pruning.
Newer data, in-flight runs, and metadata for retained runs are kept
intact.

---

## `openthomas update`

Update OpenThomas to the latest published version. Safe by design: the
trace database is snapshotted first, the new version is health-checked
after the daemon restarts, and a failed update is rolled back
automatically.

```
USAGE
  openthomas update [options]

OPTIONS
  --check    Only report whether a new version exists; do not install.
  --force    Restart immediately instead of waiting for an idle window.
```

The daemon also checks once a day on its own and surfaces a banner in
the dashboard when a new version is available. That check is a plain
request to the public npm registry — no data about you is sent (see
[`PRIVACY.md`](../PRIVACY.md)); disable it with `updateCheck: false` in
`~/.openthomas/config.json`.

The restart waits for a moment with no in-flight agent requests so a
model call is never dropped (`--force` skips the wait). After your
first update, OpenThomas asks once whether to auto-update future releases;
auto-updates are still backed up, health-gated, and auto-rolled-back.

---

## `openthomas rollback`

Revert to the version that was installed before the last `thomas
update`, restoring the database snapshot taken at that time. Use this
if a new version misbehaves in a way the automatic health check did
not catch.

```
USAGE
  openthomas rollback
```

Anything captured since that update is discarded — the pre-update
database snapshot replaces the current one. Rollback asks for
confirmation first. Snapshots live in `~/.openthomas/backups/`; the three
most recent are kept.

---

## `openthomas thomas`

Your Thomas (T) — outcome-per-token productivity score, plus the v1.1
verified companion T_verified.

```
USAGE
  openthomas thomas [options]

OPTIONS
  --period <p>     '24h' (default) | '7d' | '30d' | 'lifetime'
  --json           Output JSON for piping.
  --open           Open the dashboard in your browser instead of printing.
  --share          Open the designed PNG share card in your browser.
  --watch          Re-render every 5 seconds until interrupted (ctrl-c).
```

The metric definition is at [openthomas.com/thomas](https://openthomas.com/thomas)
(spec v1.1). The same numbers are served at
`http://localhost:9877/api/thomas?period=<p>`.

---

## `openthomas route`

Reconfigure which model(s) a wired agent's traffic actually reaches —
without touching the agent's own config. A **Control**-tier feature; free
to use in v1. Mirrors the Control · Routing pages in the UI and talks to
the running daemon, so the daemon must be up (`openthomas wire` or
`openthomas daemon`).

A route is keyed `<agent>/<provider>` (e.g. `openclaw/anthropic`). Every
route is plain **passthrough** until you opt it in.

```
USAGE
  openthomas route <subcommand>

ROUTES
  openthomas route list                       List routes and their targets.
  openthomas route show <agent>/<provider>    Show one route's routing.
  openthomas route set <agent>/<provider> --model <id> | --strategy <id> | --passthrough

PROVIDERS & MODELS
  openthomas route provider add <id> --base-url <url> --api <api>
                 [--auth passthrough|bearer|api-key] [--header <name>]
                 [--label <text>] [--key <value>]
  openthomas route provider rm <id>
  openthomas route provider list
  openthomas route model add <id> --provider <id> [--api <api>] [--vision]
                 [--reasoning] [--cost-in <usd>] [--cost-out <usd>] [--label <text>]
  openthomas route model rm <id>
  openthomas route model list

STRATEGIES  (an ordered failover chain plus tool-models)
  openthomas route strategy create <id> --member <modelId> [--member <modelId> …]
                 [--vision <modelId>] [--label <text>]
  openthomas route strategy add-member <strategyId> <modelId>
  openthomas route strategy rm <id>
  openthomas route strategy list

SECRETS  (API keys for cross-agent / vision routing; write-only)
  openthomas route secret set <ref>           Prompts for the value, no echo.
  openthomas route secret rm <ref>
  openthomas route secret list                Shows refs only — never values.
```

`--api` is the wire format: `anthropic-messages`, `openai-chat`, or
`openai-responses`. OpenThomas translates between all three, so an
Anthropic-Messages agent can be routed to an OpenAI-Chat model.

A **strategy** is a per-route composition: an ordered failover chain of
member models plus optional tool-models. A strategy created from
`--member` flags becomes an error-failover chain — the orchestrator
advances to the next member on an upstream error, and the last member is
the final fallback. `--vision` attaches a vision companion as a
tool-model, so a text-only member can still answer image requests via
the text↔multimodal split. Budget-based switch rules (*"fall back once
this model has cost > $100 this month"*) are set in the UI's strategy
builder.

API keys go into `~/.openthomas/secrets.json` (file mode `0600`). They are
sent only to the upstream URLs you configured — OpenThomas opens no new
outbound surface. The CLI and API are write-only for secrets: you can
set and remove them, but values are never read back.

---

## `openthomas tail`

Live stream of actions as they happen.

```
USAGE
  openthomas tail [options]

OPTIONS
  --agent <id>     Filter to one agent.
  --kind <k…>      Filter by action kind.
  --format <fmt>   'compact' (default) | 'json'
```

---

## `openthomas replay <run-id>`

OSS: **mock replay only.** Uses stored `raw_res` to simulate the upstream
without calling the real provider. Cost: $0.

Paid (v0.4+): live replay against real upstream.

```
USAGE
  openthomas replay <run-id> [options]

OPTIONS
  --mock           (default in OSS)
  --step           Step through actions interactively.
  --filter <k…>    Replay only certain action kinds.
```

---

## `thomas tap` (internal)

Not for users. Spawned by wired agents to wrap MCP stdio servers.

```
USAGE
  thomas tap --agent <id> --server <name> -- <command> [args…]
```

Bridges stdin / stdout between parent and child, parses JSON-RPC frames, ships
them to the daemon over UDS, fails open (passthrough) on any internal error.

---

## `openthomas daemon` (internal)

Run the daemon in the foreground. Called by launchd / systemd from the
service definition.

```
USAGE
  openthomas daemon [options]

OPTIONS
  --port <n>       Override default 9877.
  --foreground     (default when called directly)
  --log-level <l>  'debug' | 'info' | 'warn' | 'error'
```

## User-extensible model pricing

OpenThomas ships a bundled price table for Anthropic and OpenAI models. To
add prices for custom models (Xiangxin, Yi, Qwen, self-hosted, …) drop
a JSON file at `~/.openthomas/pricing.json`:

```json
{
  "openai-chat": {
    "Xiangxin-2XL-Chat": {
      "inputCostPerToken": 0.0000003,
      "outputCostPerToken": 0.00000045
    }
  },
  "any": {
    "my-custom-model": {
      "inputCostPerToken": 0.000001,
      "outputCostPerToken": 0.000002,
      "cacheReadCostPerToken": 0.0000001
    }
  }
}
```

Top-level keys are decoder kinds (`anthropic`, `openai-chat`,
`openai-responses`) or `any` to match regardless of decoder. User
entries override the bundled defaults. The daemon re-reads the file on
mtime change — no restart needed.

## See also

- [`../README.md`](../README.md) — install, supported agents, what's captured
- [`../PRIVACY.md`](../PRIVACY.md) — exact list of files OpenThomas writes and where it sends data
