> ## Documentation Index
> Fetch the complete documentation index at: https://comis-feature-skill-archive-import.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI Reference

> Complete reference for all command groups, subcommands, and flags

**What this is for:** the `comis` command is how you set up your daemon, manage agents and channels, browse memory, audit security, and operate the running system from the terminal. **Who it's for:** operators and developers running Comis on their own machine or a server.

Comis ships **24 command groups** (verified against `packages/cli/src/cli.ts`). The CLI talks to the running daemon over JSON-RPC; offline-only commands such as `secrets` operate directly on local files. Run any command with `--help` to see its full flag list — every flag below is also documented inline in the binary.

<Snippet file="invocation-modes.mdx" />

<Info>
  For installation and your first run-through, see [Quick Start](/get-started/quickstart) and [Operations](/operations). This page is the exhaustive reference; the Operations section is the friendlier task-oriented guide.
</Info>

## Your first hour with the CLI

A complete first-time setup, end to end. Each step is one command.

<Steps>
  <Step title="Initialize the workspace">
    `comis init` walks you through provider keys, the first agent, and at least one channel. Use `--non-interactive` with flags for CI/Docker.

    ```bash theme={}
    comis init
    ```
  </Step>

  <Step title="Confirm the daemon is healthy">
    `comis health` shows only failures and warnings. Exits non-zero in CI when something is wrong.

    ```bash theme={}
    comis health
    ```
  </Step>

  <Step title="Check the unified status">
    `comis status` aggregates daemon, gateway, channels, and agents into one view.

    ```bash theme={}
    comis status
    ```
  </Step>

  <Step title="Tail the daemon logs">
    `comis daemon logs -f` streams Pino JSON logs with color coding. Useful while debugging your first agent reply.

    ```bash theme={}
    comis daemon logs -f
    ```
  </Step>

  <Step title="Add a new agent">
    `comis agent create <name>` creates and persists an agent through the daemon — no manual YAML edits needed.

    ```bash theme={}
    comis agent create research-bot --provider anthropic --model claude-sonnet-4-5-20250929
    ```
  </Step>

  <Step title="Wire a channel">
    Channels are added with `comis configure --section channels` (interactive) or by editing `~/.comis/config.yaml` directly. Token values are pulled from `~/.comis/.env` via `${VAR_NAME}` substitution.

    ```bash theme={}
    comis configure --section channels
    ```
  </Step>

  <Step title="Store secrets safely">
    `comis secrets set` encrypts API keys with AES-256-GCM into `secrets.db`. `comis secrets audit` flags any plaintext secrets still sitting in your config.

    ```bash theme={}
    comis secrets set OPENAI_API_KEY
    comis secrets audit --check
    ```
  </Step>
</Steps>

After this you have a running agent reachable from at least one channel, with secrets encrypted and an auditable config history (`comis config history`).

## Global Options

These options apply across multiple commands:

| Flag                      | Type       | Default | Description                                                                                                                                                                         |
| ------------------------- | ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--format <format>`       | `string`   | `table` | Output format. Most list/show commands accept `table` or `json`. Some inspect commands accept `detail` or `json`.                                                                   |
| `-c, --config <paths...>` | `string[]` | -       | Config file paths. Supported by `config validate`, `configure`, `doctor`, `health`, `models set`, `reset`, `security audit`, and `security fix`.                                    |
| `-y, --yes`               | `boolean`  | `false` | Skip confirmation prompts for destructive operations. Supported by `agent delete`, `config rollback`, `memory clear`, `reset`, `secrets get`, `secrets delete`, and `security fix`. |

## Command Groups

<Note>
  **Not to be confused with `comis-agent`.** Every command on this page is the operator `comis` CLI: it talks to the daemon over the **gateway** with an **operator token**, runs on the **host**, and includes **admin** commands (`secrets`, `config`, `tokens`, ...). There is a separate, very different CLI named [`comis-agent`](/reference/comis-agent-cli) that an **agent drives from inside its own jail** — it authenticates with a **capability lease** (not an operator token), rides the **loopback capability Unix socket** (not the gateway), has **no admin verbs**, and is **not on the host PATH**. If you are looking for what an autonomous agent can do from inside its sandbox, see the [`comis-agent` CLI reference](/reference/comis-agent-cli) — none of the operator commands below are reachable from there.
</Note>

### `comis agent`

Agent management commands for listing, creating, updating, and deleting agent configurations.

#### Subcommands

| Subcommand               | Description                                   |
| ------------------------ | --------------------------------------------- |
| `agent list`             | List all configured agents                    |
| `agent create <name>`    | Create a new agent configuration              |
| `agent configure <name>` | Update an existing agent's settings           |
| `agent delete <name>`    | Delete an agent configuration                 |
| `agent models <agentId>` | Show operation model resolutions for an agent |

#### `agent list`

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

#### `agent create <name>`

| Flag                    | Type     | Default | Description                                      |
| ----------------------- | -------- | ------- | ------------------------------------------------ |
| `--provider <provider>` | `string` | -       | AI provider (e.g. `anthropic`, `openai`)         |
| `--model <model>`       | `string` | -       | Model to use (e.g. `claude-sonnet-4-5-20250929`) |

#### `agent configure <name>`

| Flag                    | Type     | Default | Description  |
| ----------------------- | -------- | ------- | ------------ |
| `--provider <provider>` | `string` | -       | AI provider  |
| `--model <model>`       | `string` | -       | Model to use |

#### `agent delete <name>`

| Flag        | Type      | Default | Description              |
| ----------- | --------- | ------- | ------------------------ |
| `-y, --yes` | `boolean` | `false` | Skip confirmation prompt |

#### `agent models <agentId>`

Show how each agent operation resolves to a concrete model: primary model, tiered operation overrides, source (config or default), per-operation timeouts, cross-provider flag, and API-key availability.

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

```bash theme={}
# List all agents
comis agent list

# Create an agent with a specific provider and model
comis agent create my-agent --provider anthropic --model claude-sonnet-4-5-20250929

# Delete an agent without confirmation
comis agent delete old-agent --yes
```

***

### `comis auth`

OAuth authentication management for subscription-based providers (currently OpenAI Codex). Profiles are written to the [OAuthCredentialStorePort](/security/oauth#storage-backends) — separate from `SecretStorePort`, with refresh + per-profile-ID locking semantics.

#### Subcommands

| Subcommand    | Description                                                       |
| ------------- | ----------------------------------------------------------------- |
| `auth login`  | Log in to an OAuth-enabled provider (browser or device-code flow) |
| `auth list`   | List stored OAuth profiles                                        |
| `auth logout` | Remove a stored OAuth profile                                     |
| `auth status` | Show per-provider OAuth state (expiry, last refresh)              |

#### `auth login`

| Flag                | Type                       | Default    | Description                                                                                                                     |
| ------------------- | -------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `--provider <id>`   | `string`                   | *required* | OAuth provider id. Must be `openai-codex`.                                                                                      |
| `--remote`          | `boolean`                  | *auto*     | Force remote/headless mode (no browser open). Auto-detected via `SSH_CLIENT`, `SSH_TTY`, missing `DISPLAY`.                     |
| `--local`           | `boolean`                  | *auto*     | Force local/desktop mode (attempt browser open). Inverse of `--remote`.                                                         |
| `--profile <id>`    | `string`                   | *derived*  | Override the auto-derived profile id (`<provider>:<email-from-jwt>`). Provider portion of the override must match `--provider`. |
| `--method <method>` | `browser` \| `device-code` | `browser`  | Login method. Use `device-code` for SSH/no-clipboard environments.                                                              |

```bash theme={}
# Local desktop (browser auto-open)
comis auth login --provider openai-codex

# Headless / SSH host (device-code flow — short code displayed; type it on a phone)
comis auth login --provider openai-codex --method device-code

# Multi-account: tag the new profile with a custom alias
comis auth login --provider openai-codex --profile openai-codex:work@company.com
```

**Exit codes (`auth login`):**

| Code | Meaning                                                                                                                                                      |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `0`  | Login completed; profile persisted                                                                                                                           |
| `1`  | Login flow failed (network, OAuth server error, store-write failure)                                                                                         |
| `2`  | Invalid arguments (`--provider` not `openai-codex`; malformed `--profile`; provider mismatch in `--profile`; `--method device-code` on a non-codex provider) |

#### `auth list`

| Flag              | Type     | Default | Description             |
| ----------------- | -------- | ------- | ----------------------- |
| `--provider <id>` | `string` | -       | Filter to one provider. |

```bash theme={}
# All profiles
comis auth list

# Only Codex profiles
comis auth list --provider openai-codex
```

**Exit codes:** `0` success (including empty store with informative message); `1` config load / store open failure.

#### `auth logout`

| Flag             | Type     | Default    | Description                                                   |
| ---------------- | -------- | ---------- | ------------------------------------------------------------- |
| `--profile <id>` | `string` | *required* | Profile id to remove (e.g., `openai-codex:user@example.com`). |

```bash theme={}
comis auth logout --profile openai-codex:user@example.com
```

**Exit codes:** `0` profile removed; `1` profile not found in store.

#### `auth status`

| Flag              | Type     | Default | Description             |
| ----------------- | -------- | ------- | ----------------------- |
| `--provider <id>` | `string` | -       | Filter to one provider. |

Reports per-provider state: profile id, identity (email or accountId), expiry timestamp, status (`active` / `expired`), and last refresh time.

```bash theme={}
comis auth status
comis auth status --provider openai-codex
```

<Tip>
  Errors from any `auth` subcommand are classified into 6 categories — see [OAuth concepts → Error classification](/security/oauth#error-classification). The CLI prints both `userMessage` and an actionable `hint` (e.g., `Re-authenticate with: comis auth login --provider openai-codex`).
</Tip>

***

### `comis cache`

Inspect aggregated token-usage cache statistics stored in the durable `obs_token_usage` SQLite database. Dispatches the `obs.cacheStats.window` RPC.

#### Subcommands

| Subcommand    | Description                                           |
| ------------- | ----------------------------------------------------- |
| `cache stats` | Display aggregated cache statistics for a time window |

#### `comis cache stats` flags

| Flag                    | Type                            | Default | Description                                                             |
| ----------------------- | ------------------------------- | ------- | ----------------------------------------------------------------------- |
| `--since <window>`      | string                          | `24h`   | Window start: `1h`, `24h`, `7d`, `30d`, or any `Nh`/`Nd`/`Nw`/`Nm`/`Ny` |
| `--until <iso>`         | string                          | now     | Window end (ISO-8601)                                                   |
| `--agent <agentId>`     | string                          | —       | Filter results by agent ID                                              |
| `--provider <provider>` | string                          | —       | Filter results by provider name                                         |
| `--format <format>`     | `table` \| `json` \| `markdown` | `table` | Output format                                                           |

```bash theme={}
# Show cache stats for the last 24 hours (default)
comis cache stats

# Show stats for the last 7 days, filtered by provider
comis cache stats --since 7d --provider anthropic

# Machine-readable output
comis cache stats --format json
```

***

### `comis channel`

Channel management commands for viewing the connection status of configured chat platform adapters.

#### Subcommands

| Subcommand       | Description                       |
| ---------------- | --------------------------------- |
| `channel status` | Display channel connection status |

#### `channel status`

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

```bash theme={}
# View channel connection status
comis channel status
```

***

### `comis config`

Configuration management commands for validating, viewing, modifying, and version-controlling the config file.

#### Subcommands

| Subcommand                   | Description                                                                  |
| ---------------------------- | ---------------------------------------------------------------------------- |
| `config validate`            | Validate configuration files against the schema                              |
| `config show [section]`      | Display current configuration (optionally filtered to a section)             |
| `config set <path> <value>`  | Modify a config value via the daemon (triggers restart)                      |
| `config history`             | Display config change history (git-backed)                                   |
| `config diff [sha]`          | Display config diff against HEAD or a specific commit                        |
| `config rollback <sha>`      | Restore config from a previous commit                                        |
| `config sync-tooling`        | Discover MCPs/skills and sync the `tooling:` config block                    |
| `config tooling-fill [hint]` | Fill `description` + `replacesPackages` on a tooling hint via the live agent |

#### `config validate`

| Flag                      | Type       | Default                               | Description                   |
| ------------------------- | ---------- | ------------------------------------- | ----------------------------- |
| `-c, --config <paths...>` | `string[]` | From `COMIS_CONFIG_PATHS` or defaults | Config file paths to validate |

#### `config show [section]`

| Flag                | Type               | Default  | Description   |
| ------------------- | ------------------ | -------- | ------------- |
| `--format <format>` | `detail` \| `json` | `detail` | Output format |

#### `config set <path> <value>`

No additional flags. The path uses dot-notation (e.g. `gateway.port 5000`). Values are parsed as JSON first, falling back to string.

#### `config history`

| Flag                | Type              | Default | Description                |
| ------------------- | ----------------- | ------- | -------------------------- |
| `--limit <n>`       | `string`          | `10`    | Maximum entries to display |
| `--format <format>` | `table` \| `json` | `table` | Output format              |

#### `config diff [sha]`

No additional flags. Displays a colorized diff of the current config against HEAD or a specific commit SHA.

#### `config rollback <sha>`

| Flag        | Type      | Default | Description              |
| ----------- | --------- | ------- | ------------------------ |
| `-y, --yes` | `boolean` | `false` | Skip confirmation prompt |

```bash theme={}
# Validate config files
comis config validate -c ~/.comis/config.yaml

# Show the gateway section
comis config show gateway

# Set a config value (triggers daemon restart)
comis config set gateway.port 5000

# View config change history
comis config history --limit 5

# Rollback to a previous config version
comis config rollback abc1234 --yes
```

#### `config sync-tooling`

Discovers connected MCP servers (from `integrations.mcp.servers[].name`) and installed skills (walks `agents.<id>.skills.discoveryPaths` plus the daemon defaults `~/.comis/skills` and `~/.comis/workspace/skills`) and materializes the `tooling:` block in `config.yaml`.

| Flag                  | Type              | Default                | Description                                                                                                                                                                                                                                                                 |
| --------------------- | ----------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--write`             | `boolean`         | `false`                | Apply the diff atomically to `config.yaml`; writes a timestamped backup beside the active config first. **Daemon must be stopped** — `tooling.*` is restart-required.                                                                                                       |
| `--overwrite`         | `boolean`         | `false`                | With `--write`, regenerate the entire managed sub-tree. Preserves operator-authored `tooling.capabilityClusters.clusters` byte-for-byte; wipes and rebuilds `mcp.capabilityHints`, `skills.capabilityHints`, `installDetours`, `capabilityIndex`. Errors without `--write`. |
| `--format <format>`   | `human` \| `json` | `human`                | Output format for inspect mode (default — no flags).                                                                                                                                                                                                                        |
| `-c, --config <path>` | `string`          | `~/.comis/config.yaml` | Config file path.                                                                                                                                                                                                                                                           |

**Modes:**

* **Inspect (default)** — prints the discovered/existing/diff/wouldWrite shape to stdout; never touches `config.yaml`. Daemon may be running.
* **`--write`** — atomic write with `config.pre-sync-tooling-<ISO>-<hex>.yaml` backup. Append-only on existing keys (operator hand-edits to `description` / `replacesPackages` are preserved); stale hints (MCPs/skills no longer in discovery) are pruned.
* **`--write --overwrite`** — full regenerate of the managed sub-tree, plus the destructive-overwrite warning.

**Stub-emission contract:** newly-generated MCP and skill hints land with `description: TODO`, `replacesPackages: []`, and a `# TODO: list npm/pip packages this MCP replaces` comment. Operators fill these by hand or via `config tooling-fill` (below). Until they're filled, the install-detour subsystem cannot fire on alias overlaps for that hint.

**Exit codes:**

| Code | Meaning                                                                                                                        |
| ---- | ------------------------------------------------------------------------------------------------------------------------------ |
| `0`  | Success (inspect printed; or `--write` applied / no-op)                                                                        |
| `1`  | Daemon-active guard fired (daemon running with `--write`); usage error (`--overwrite` without `--write`); LLM-related failures |
| `2`  | Backup or atomic-write failure                                                                                                 |
| `3`  | Config parse error (invalid YAML in `config.yaml`)                                                                             |

```bash theme={}
# Inspect what would change (safe, daemon may be running)
comis config sync-tooling

# JSON output for scripting
comis config sync-tooling --format json

# Apply (daemon must be stopped first)
systemctl stop comis
comis config sync-tooling --write
systemctl start comis

# Full regenerate (destructive of managed sub-tree, preserves operator clusters)
systemctl stop comis
comis config sync-tooling --write --overwrite
systemctl start comis
```

#### `config tooling-fill [hint-name]`

Fills the `description` and `replacesPackages` fields of a tooling capability hint via the live Comis agent. Automates the LLM call, the daemon-stop window, the AST-aware YAML edit, and the daemon-restart cycle. Use after `config sync-tooling --write` has materialized stubs.

| Flag                  | Type              | Default                | Description                                                                                                                                                                 |
| --------------------- | ----------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--all`               | `boolean`         | `false`                | Fill every stub-valued hint in one daemon-restart cycle. Skips operator-filled hints silently unless `--force` is also set.                                                 |
| `--force`             | `boolean`         | `false`                | Overwrite hints that already have non-stub values. Required to refill an operator-edited hint.                                                                              |
| `--dry-run`           | `boolean`         | `false`                | Call the agent, print the suggested values + diff, exit 0. Never stops the daemon, never writes the file.                                                                   |
| `--yes`               | `boolean`         | `false`                | Skip the values-confirmation prompt. Required for non-TTY runs.                                                                                                             |
| `--restart`           | `boolean`         | `false`                | Authorize the daemon stop+start window. Required for non-TTY runs. `--allow-restart` is an accepted alias.                                                                  |
| `--no-restart`        | `boolean`         | `false`                | Write the file but skip the daemon stop+start cycle. Mutually exclusive with `--allow-restart`.                                                                             |
| `--restart-cmd <cmd>` | `string`          | (auto-detect)          | Override the supervisor with a literal shell command. Use for non-standard setups (custom init, container runtime, etc.). The command runs once, after the file is written. |
| `--force-no-validate` | `boolean`         | `false`                | Skip the npm/pip name-shape regex on `replacesPackages`. Escape hatch — emits a loud warning.                                                                               |
| `-c, --config <path>` | `string`          | `~/.comis/config.yaml` | Config file path.                                                                                                                                                           |
| `--agent <id>`        | `string`          | (daemon's first agent) | Agent ID for the LLM call.                                                                                                                                                  |
| `--kind <kind>`       | `mcp` \| `skills` | (auto-resolved)        | Disambiguate hint kind when the same name appears in both `tooling.mcp.capabilityHints` and `tooling.skills.capabilityHints`.                                               |

**Operating principles:**

* Daemon must be **up** for the LLM call (the CLI POSTs to `/api/chat` on the local gateway). The CLI then stops the daemon, mutates the file atomically, writes a `config.pre-tooling-fill-<ISO>-<hex>.yaml` backup, and restarts.
* After `systemctl start` (or the equivalent) succeeds, the orchestrator polls `/api/system.ping` for up to 15 s to confirm the daemon actually came up — not just that the unit was queued. If the daemon crashed during boot (e.g. invalid YAML), the orchestrator restores the backup and exits 2 with a manual-recovery hint.
* File ownership of `config.yaml` is preserved across the atomic write, even when run as root on behalf of an unprivileged service user (the typical VPS setup with `comis` user under systemd).
* Backups older than the 5 most recent are pruned automatically after a successful run. The `tooling-fill` and `sync-tooling` prefixes prune independently.
* The agent's response is restricted to a strict 2-line format (`DESCRIPTION:` and `REPLACES_PACKAGES:`); any other field is stripped from the parsed output. Package names that fail the npm/pip name-shape regex are silently dropped (or surface as a hard failure if all are dropped).

**Idempotency:** A hint is "stub-valued" iff `description ∈ {missing, "", "TODO"}` AND `replacesPackages ∈ {missing, []}`. Stub-valued hints fill freely; hints with any operator-authored value refuse without `--force`.

**Exit codes:**

| Code | Meaning                                                                                                                                                                          |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`  | Success (filled, dry-run, or operator declined at confirmation prompt)                                                                                                           |
| `1`  | Idempotency guard fired without `--force`; non-TTY without `--yes`/`--restart`; daemon unreachable; LLM call failed; all package names invalid                                   |
| `2`  | Backup, atomic-write, validate, or post-restart liveness failure (rolled back to backup state — message includes the backup path and recovery command if rollback itself failed) |

```bash theme={}
# Fill a single hint interactively (prompts for values + restart)
comis config tooling-fill yfinance

# Non-interactive (CI / scripts)
comis config tooling-fill yfinance --yes --restart

# Preview what the agent would suggest, without touching anything
comis config tooling-fill yfinance --dry-run

# Fill all stub-valued hints in one daemon-restart cycle
comis config tooling-fill --all --yes --restart

# Overwrite an operator-edited hint (force replace)
comis config tooling-fill yfinance --force --yes --restart

# Custom supervisor (e.g. a container restart command)
comis config tooling-fill yfinance --yes --restart-cmd "docker compose restart comis"

# Write the file but skip the restart (operator handles it manually)
comis config tooling-fill yfinance --yes --no-restart && systemctl restart comis
```

<Note>
  `comis config tooling-fill` requires the daemon to be **running** at invocation time (the LLM call goes through the local gateway). It then stops the daemon, writes the file, and restarts. This inverts the daemon-state requirement of `config sync-tooling --write` (which requires the daemon to be **stopped** at invocation time).
</Note>

***

### `comis configure`

Interactive configuration editor using terminal prompts. Walks through editable config fields section by section, validates changes against the Zod schema, and writes back to the YAML file preserving formatting and comments.

| Flag                  | Type     | Default                  | Description                         |
| --------------------- | -------- | ------------------------ | ----------------------------------- |
| `-c, --config <path>` | `string` | `/etc/comis/config.yaml` | Config file path                    |
| `--section <section>` | `string` | -                        | Jump directly to a specific section |

<Info>Requires an interactive terminal (TTY). Use `comis config set` for non-interactive config changes.</Info>

```bash theme={}
# Launch interactive config editor
comis configure

# Edit a specific section directly
comis configure --section gateway
```

***

### `comis daemon`

Control the Comis daemon process. Uses systemd if available, otherwise falls back to direct process management with PID file tracking.

#### Subcommands

| Subcommand      | Description            |
| --------------- | ---------------------- |
| `daemon start`  | Start the Comis daemon |
| `daemon stop`   | Stop the Comis daemon  |
| `daemon status` | Show daemon status     |
| `daemon logs`   | Show daemon logs       |

#### `daemon start`

No additional flags. Starts via systemd if installed, otherwise spawns a detached Node.js process and waits for the gateway health endpoint to respond.

#### `daemon stop`

No additional flags. Sends SIGTERM (graceful), escalates to SIGKILL after 10 seconds if the process does not exit.

#### `daemon status`

No additional flags. Checks status via RPC first, then systemd, then PID file.

#### `daemon logs`

| Flag              | Type      | Default | Description                                       |
| ----------------- | --------- | ------- | ------------------------------------------------- |
| `-f, --follow`    | `boolean` | `false` | Follow log output in real-time                    |
| `-n, --lines <n>` | `string`  | `50`    | Number of lines to show                           |
| `--raw`           | `boolean` | `false` | Show raw JSON log records without Pino formatting |

```bash theme={}
# Start the daemon
comis daemon start

# Follow logs in real-time
comis daemon logs -f

# Show last 100 log lines
comis daemon logs -n 100
```

***

### `comis doctor`

Run diagnostic health checks across nine categories: configuration, daemon, gateway, version, channels, workspace, oauth, secrets-audit, and LCD store. Supports auto-repair mode for fixable issues. The `oauth` category covers JWT expiry, schema mismatch, ca-certificates, HTTPS\_PROXY drift, and TLS preflight — see [OAuth concepts → Error classification](/security/oauth#error-classification).

The `version` category compares the running `comis` CLI version against the daemon's reported version (read from the `gateway.status` RPC) and **warns on a mismatch** — with a stronger message on a major.minor skew. This catches a stale global `comis` (e.g. an old `npm i -g comisai`) earlier on `PATH` than a freshly-built daemon: an out-of-date CLI validates config against an OLD schema and reports **phantom failures** (a valid key flagged "Unrecognized"). It passes when the versions match and skips cleanly (never fails) when the daemon is unreachable or its status response carries no version field.

All checks share one config resolution that mirrors daemon boot: `${VAR}` references are substituted from the process environment, then `~/.comis/.env`, then the encrypted secret store (offline read). A reference none of them resolves is reported with its config path and var name, and the channel check fails an enabled channel whose credential reference did not resolve — encrypted-store deployments validate cleanly without exporting secrets into the environment.

**LCD Store checks** — when `memory.db` is present, six read-only scan classes run automatically against the lossless context store: orphaned summaries, dangling `context_items` references, fallback-marker summaries (quality debt), FTS row-count drift, R4 scope anomalies (NULL tenant/agent keys), and `lcd_ingest_cursor` over-count (a cursor claiming more ingested messages than the store holds). Output is content-free: findings carry only counts and row ids, never message text. Silently skipped on new installs where `memory.db` does not exist yet.

| Flag                      | Type              | Default | Description                                                                                               |
| ------------------------- | ----------------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `--repair`                | `boolean`         | `false` | Auto-fix repairable issues                                                                                |
| `-c, --config <paths...>` | `string[]`        | -       | Config file paths to check                                                                                |
| `--format <format>`       | `table` \| `json` | `table` | Output format                                                                                             |
| `--refresh-test`          | `boolean`         | `false` | OAuth: actively test refresh against the provider. **Warning:** this rotates the refresh token at OpenAI. |

```bash theme={}
# Run full diagnostics
comis doctor

# Auto-repair fixable issues
comis doctor --repair -c ~/.comis/config.yaml
```

**`--repair` flag — LCD store repair actions**

When `--repair` is passed, actionable findings are automatically repaired offline (no daemon required). Two LCD finding categories support offline repair:

| Finding                       | Repair action                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Dangling `context_items` refs | Stale `lcd_context_items` entries whose `ref_id` points to a deleted summary or message are removed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| FTS row-count drift           | `lcd_summaries_fts` (external-content) is rebuilt via the FTS5 `rebuild` command. `lcd_messages_fts` (self-contained — it stores its own re-rendered content, not a reference into `lcd_message_parts`) is repopulated by re-rendering each message's parts into the index. The three trigram twins (`lcd_messages_fts_tri`, `lcd_summaries_fts_tri`, `memory_fts_tri`) — the multilingual search lane — are repopulated with **normalized** text (the same per-script fold applied at write time, so a Hebrew/Arabic/Cyrillic/CJK query matches pre-existing history), making rows the trigram twins had not yet indexed searchable. Skipped on hosts whose SQLite lacks the trigram tokenizer. |

**Fallback-marker summaries (`fallback=1`) are not repairable offline.** They are quality debt produced when the LLM summarizer was unavailable — re-summarization requires the running daemon. The daemon re-summarizes fallback-marker summaries automatically during normal compaction as conversations are processed.

Raw message history (`lcd_messages`) is **never modified** by any repair action — the lossless verbatim raw store is read-only from the repair path.

<Warning>
  **Doctor repair requires the daemon to be stopped.** The repair opens `memory.db` in read-write mode. If the daemon is running, the repair will return `SQLITE_BUSY`.

  **Always run `comis sessions backup` before `comis doctor --repair`** to ensure a recovery point exists before any writes to `memory.db`.
</Warning>

***

### `comis health`

Quick view of system health issues. Unlike `doctor` which shows all checks, `health` shows only failures and warnings by default. Exits with code 1 when failures exist, making it suitable for CI pipelines.

| Flag                      | Type              | Default | Description                                |
| ------------------------- | ----------------- | ------- | ------------------------------------------ |
| `-c, --config <paths...>` | `string[]`        | -       | Config file paths                          |
| `--format <format>`       | `table` \| `json` | `table` | Output format                              |
| `--all`                   | `boolean`         | `false` | Show all findings including passing checks |

```bash theme={}
# Quick health check
comis health

# Show all checks including passing ones
comis health --all

# JSON output for CI
comis health --format json
```

***

### `comis init`

Interactive setup wizard for first-time configuration. Supports three modes: interactive wizard (default), non-interactive with flags for CI/Docker, and JSON output for automation.

#### Key Flags

| Flag                | Type      | Default | Description                                                |
| ------------------- | --------- | ------- | ---------------------------------------------------------- |
| `--non-interactive` | `boolean` | `false` | No prompts, all values from flags                          |
| `--accept-risk`     | `boolean` | `false` | Acknowledge security notice (required for non-interactive) |
| `--quick`           | `boolean` | `false` | Skip flow selection, use QuickStart                        |
| `--json`            | `boolean` | `false` | Output result as JSON                                      |

#### Provider and Credentials

| Flag                  | Type     | Default       | Description                               |
| --------------------- | -------- | ------------- | ----------------------------------------- |
| `--provider <id>`     | `string` | -             | LLM provider (e.g. `anthropic`, `openai`) |
| `--api-key <key>`     | `string` | -             | Provider API key                          |
| `--agent-name <name>` | `string` | `comis-agent` | Agent identifier                          |
| `--model <id>`        | `string` | -             | Model identifier                          |

#### Gateway

| Flag                    | Type                            | Default    | Description            |
| ----------------------- | ------------------------------- | ---------- | ---------------------- |
| `--gateway-port <n>`    | `number`                        | `4766`     | Gateway port           |
| `--gateway-bind <mode>` | `loopback` \| `lan` \| `custom` | `loopback` | Bind mode              |
| `--gateway-token <tok>` | `string`                        | -          | Explicit gateway token |

#### Channels

| Flag                      | Type     | Default | Description                  |
| ------------------------- | -------- | ------- | ---------------------------- |
| `--channels <list>`       | `string` | -       | Comma-separated channel list |
| `--telegram-token <tok>`  | `string` | -       | Telegram bot token           |
| `--discord-token <tok>`   | `string` | -       | Discord bot token            |
| `--slack-bot-token <tok>` | `string` | -       | Slack bot token              |
| `--slack-app-token <tok>` | `string` | -       | Slack app token              |
| `--line-token <tok>`      | `string` | -       | LINE channel token           |
| `--line-secret <sec>`     | `string` | -       | LINE channel secret          |

#### Media Generation and Processing

The interactive **advanced** flow includes dedicated steps that offer every supported provider for each media feature: **Image Generation** ([`integrations.media.imageGeneration.provider`](/reference/config-yaml)), **Video Generation** ([`...videoGeneration.provider`](/reference/config-yaml)), **Voice Transcription** ([`...transcription.provider`](/reference/config-yaml)), and **Text-to-Speech** ([`...tts.provider`](/reference/config-yaml)). The same selections are available non-interactively via the flags below. (TTS provider setup moved here from the tool-providers step, which is now web-search only, so a key is never requested twice.)

| Flag                    | Type                                                                      | Default  | Description                                                 |
| ----------------------- | ------------------------------------------------------------------------- | -------- | ----------------------------------------------------------- |
| `--image-provider <id>` | `auto` \| `fal` \| `openai` \| `openai-codex` \| `google` \| `openrouter` | `auto`   | Image generation provider                                   |
| `--image-api-key <key>` | `string`                                                                  | -        | Image provider credential (e.g. `FAL_KEY`)                  |
| `--video-provider <id>` | `auto` \| `fal` \| `google` \| `xai`                                      | `auto`   | Video generation provider                                   |
| `--video-api-key <key>` | `string`                                                                  | -        | Video provider credential (e.g. `FAL_KEY`)                  |
| `--stt-provider <id>`   | `openai` \| `groq` \| `deepgram`                                          | `openai` | Voice transcription provider                                |
| `--stt-api-key <key>`   | `string`                                                                  | -        | Transcription provider credential (e.g. `DEEPGRAM_API_KEY`) |
| `--tts-provider <id>`   | `openai` \| `elevenlabs` \| `edge`                                        | `openai` | Text-to-speech provider                                     |
| `--tts-api-key <key>`   | `string`                                                                  | -        | TTS provider credential (e.g. `ELEVENLABS_API_KEY`)         |

Credentials follow the provider-following model: `auto` (image/video) reuses the agent's main provider key; `openai`/`google`/`openrouter`/`xai`/`groq` reuse `OPENAI_API_KEY`/`GOOGLE_API_KEY`/`OPENROUTER_API_KEY`/`XAI_API_KEY`/`GROQ_API_KEY` when the main provider already supplies them; `openai-codex` (image) uses your Codex OAuth login; and `edge` (TTS) is free. So the `--*-api-key` flags are only needed for a dedicated backend (`fal`, `deepgram`, `elevenlabs`) or a cross-provider static-key choice. See [Environment Variables](/reference/environment-variables#llm-provider-keys) for the key reuse rules.

#### Paths and Behavior

| Flag                    | Type                                 | Default    | Description                        |
| ----------------------- | ------------------------------------ | ---------- | ---------------------------------- |
| `--data-dir <path>`     | `string`                             | `~/.comis` | Workspace directory                |
| `--config-dir <dir>`    | `string`                             | -          | Override config directory          |
| `--start-daemon`        | `boolean`                            | `false`    | Auto-start daemon after config     |
| `--skip-health`         | `boolean`                            | `false`    | Skip post-setup health check       |
| `--skip-validation`     | `boolean`                            | `false`    | Skip API key validation            |
| `--reset`               | `boolean`                            | `false`    | Reset existing config before setup |
| `--reset-scope <scope>` | `config` \| `config+creds` \| `full` | `config`   | Reset scope                        |

<Tip>Run `comis init --help` for the complete list of all flags.</Tip>

```bash theme={}
# Interactive setup wizard
comis init

# Non-interactive setup for CI/Docker
comis init --non-interactive --accept-risk \
  --provider anthropic --api-key "$ANTHROPIC_API_KEY" \
  --start-daemon --json
```

#### Non-interactive rejection: `--provider openai-codex`

`comis init --non-interactive --provider openai-codex` is **deliberately rejected** because the Codex OAuth flow requires interactive UI (browser callback, device-code prompt, or manual paste). The CLI exits non-zero with the literal error:

> openai-codex requires interactive login; run `comis init` interactively or run `comis auth login --provider openai-codex --method device-code` separately.

For headless / CI environments, you have three options:

1. Run `comis init` interactively (in a TTY).
2. Run `comis init --non-interactive` with a different provider, then run [`comis auth login --method device-code`](#comis-auth) afterwards to add the Codex profile.
3. Pre-seed the OAuth credential via the [`OAUTH_OPENAI_CODEX`](/reference/environment-variables#oauth_openai_codex) environment variable on the daemon's first start. The daemon's env-var bootstrap path is read once when the credential store is empty for that provider; subsequent drift triggers a one-shot WARN.

```bash theme={}
# Wrong — exits non-zero with the error above
comis init --non-interactive --provider openai-codex

# Right — drop --provider and add the profile interactively afterward
comis init --non-interactive
comis auth login --provider openai-codex --method device-code
```

***

### `comis memory`

Memory management and recall-diagnostics commands for searching, inspecting, and clearing conversation memory entries, and for inspecting how hybrid recall ranked them.

#### Subcommands

| Subcommand                      | Description                                                                                                                 |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `memory search <query>`         | Search memory entries by content                                                                                            |
| `memory inspect <id>`           | Display full details of a memory entry                                                                                      |
| `memory stats`                  | Display memory statistics (with a best-effort recall-counter overlay)                                                       |
| `memory learning`               | Outcome-learning telemetry: coverage, volume by source, success/failure ratio per agent                                     |
| `memory skills`                 | Procedural-learning telemetry: the learned-skill admission funnel (counts by state, per agent) + promotion/demotion roll-up |
| `memory recall-trace <session>` | Inspect a session's hybrid-recall trace — **admin**                                                                         |
| `memory observations`           | List observation provenance (sources + history) — **admin**                                                                 |
| `memory entities`               | List an agent's entity graph, most-mentioned first — **admin**                                                              |
| `memory clear`                  | Clear memory entries matching a filter                                                                                      |
| `memory export`                 | Export agent memory to a secret-scrubbed versioned JSON file — **admin**                                                    |
| `memory import <file>`          | Import memory from a `comis-memory-export-v1` JSON envelope — **admin**                                                     |
| `memory pin <id>`               | Pin a memory entry so it is always injected in recall — **admin**                                                           |
| `memory unpin <id>`             | Remove the always-inject pin from a memory entry — **admin**                                                                |

<Note>
  `memory recall-trace`, `memory observations`, `memory entities`, `memory pin`, and `memory unpin` are **admin-gated** and **(tenant, agent)-scoped** — they require an admin-scoped gateway token and operate within the caller's tenant/agent scope.
</Note>

#### `memory search <query>`

| Flag                | Type              | Default | Description               |
| ------------------- | ----------------- | ------- | ------------------------- |
| `--limit <n>`       | `string`          | `10`    | Maximum results to return |
| `--format <format>` | `table` \| `json` | `table` | Output format             |

#### `memory inspect <id>`

| Flag                | Type               | Default  | Description   |
| ------------------- | ------------------ | -------- | ------------- |
| `--format <format>` | `detail` \| `json` | `detail` | Output format |

Accepts a full id or an id prefix. Resolution scans the 1000 most recent entries (via `memory.browse`); for older entries use `comis memory export`.

#### `memory stats`

| Flag                | Type               | Default  | Description   |
| ------------------- | ------------------ | -------- | ------------- |
| `--format <format>` | `detail` \| `json` | `detail` | Output format |

<Note>
  `memory stats` overlays best-effort **recall counters** on top of the base memory statistics: lane usage, rerank-fallback rate, consolidation throughput, and recall hit-rate. The overlay is best-effort — a daemon that has not wired the counters (or a non-admin caller) still renders base stats.
</Note>

#### `memory learning`

Outcome-learning telemetry for the verified-learning signal: the **coverage** (the share of finished trajectories that produced a resolvable outcome), the **volume by source**, and the **success/failure ratio**, broken down per agent. Read **offline** from the local [`outcome_events` ledger](/operations/data-directory) in `~/.comis/memory.db` (no gateway required). **Counts only** — no message bodies, no confidence values, no recalled/skill ids.

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

```bash theme={}
comis memory learning
comis memory learning --format json
```

<Note>
  Outcome capture is **per-agent default-off** (`agents.<id>.learningOutcome.enabled`, gated by the master switch `memory.enabled`). With no recorded events the command prints an honest "no outcome events recorded yet" message naming the config keys to enable, rather than a misleading empty table. The same `learning` signal is reconstructable per session via [`comis explain`](#comis-explain).

  The wrongness-based **forgetting** sweep emits its own **counts-only** trajectory signals — `learning:memory_demoted` / `learning:memory_evicted` (soft-eviction counts), a once-per-run `learning:lifecycle_swept` summary (`scanned`/`promoted`/`demoted`/`evicted`), and `learning:memory_failure_attributed` (the corroborated-failure accrual that precedes eviction) — surfaced through [`comis explain`](#comis-explain) (per session), [`comis fleet`](#comis-fleet) (the daemon-wide `memory_lifecycle` finding, fleet-wide), and `cron.runs jobName "Memory lifecycle"` (the per-sweep `scanned/evicted/demoted` counts). It is part of the one learning layer (`agents.<id>.learning.enabled`, also under `memory.enabled`), reads `learning.forget.*`, and carries **no memory body** — see [Learning](/reference/config-yaml#learning-agents-learning). (Recall ranking is the fixed `rag.scoring` fusion — there is no learned recall weight.)
</Note>

#### `memory skills`

Reflection-engine telemetry for the Verified Learning loop: the **learned-doc admission funnel** — the total count and the per-state breakdown (`candidate` / `active` / `stale` / `archived`), broken down per agent, plus the per-doc `name·state·proof-count` (with `confidence` and a `mutating` flag). It also reports the **promotion / demotion roll-up** — `Promoted (active)` (the count that reached `active`) and `Demoted (stale+archived)` (the count that left `active`), store-wide and per agent — derived from the per-state tally. Read **offline** from the `kind='skill'` rows of the local [`mental_models` table](/operations/data-directory#learned-skills-verified-learning) in `~/.comis/memory.db` (no gateway required). **Counts, ids, and closed enums only** — never a procedure body, script, or description (the same closed-graph firewall as the trajectory signals). The `active` count is what the agent can actually use: an `active`, read-only procedure is surfaced into `<available_skills>` (default-off), while `candidate` / `stale` / `archived` rows are admitted-but-not-surfaced. See [Learning in config-yaml](/reference/config-yaml#learning-agents-learning).

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

```bash theme={}
comis memory skills
comis memory skills --format json
```

<Note>
  The reflection engine is **per-agent default-off** (`agents.<id>.learning.enabled`, gated by the master switch `memory.enabled`). With no learned docs the command prints an honest "no learned skills yet" message naming the config keys to enable, rather than a misleading empty table. Each reflection run emits **counts-only** trajectory signals — `reflect:admitted` (the admitted-doc count) and `reflect:funnel` (the whole funnel: `synthesized` / `validated` / `admitted` / `maxClusterCardinality` plus a content-free **`admissionOutcome`** closed enum — `reflectOutcome` ∈ {`admitted`, `no_successes`, `untrusted_origin`, `uncorroborated`, `empty_reflection`, `rejected_name_length`, `rejected_validation`} — so `comis explain` answers "why was 0 admitted" from one field) — surfaced through [`comis explain`](#comis-explain) (per session) and [`comis fleet`](#comis-fleet) (fleet-wide); a learned procedure used in a failed/corrected trajectory or a low-capability **abstain** is named by the `learned_skill_failing` / `synthesis_abstained_low_capability` root-cause verdicts (the abstain is **benign** — Defer ≠ Retry).
</Note>

#### `memory recall-trace <session>`

Inspect the hybrid-recall trace for a session — the per-recall ranking previews recorded by the opt-in [`diagnostics.recallTrace`](/reference/config-yaml) artifact. **Admin-gated** and (tenant, agent)-scoped.

| Flag                | Type              | Default | Description                                           |
| ------------------- | ----------------- | ------- | ----------------------------------------------------- |
| `--trace-id <id>`   | `string`          | -       | Filter by trace id instead of / alongside the session |
| `--agent <agentId>` | `string`          | -       | Scope to a specific agent                             |
| `--limit <n>`       | `string`          | `200`   | Maximum records to return                             |
| `--format <format>` | `table` \| `json` | `table` | Output format                                         |

<Note>
  The **table** view shows the correlation keys and the final-count summary (`trace`, `session`, `finalCount`, `ts`) — enough to locate the right record. The full per-record ranking breakdown (matched lanes, fused order, pre/post-rerank scores, score components, include/exclude reasons) is available via `--format json`. See the [recall runbook](/operations/troubleshooting#memory-recall-issues).
</Note>

#### `memory observations`

List observation provenance — the consolidated observations and the sources + history behind them. **Admin-gated** and (tenant, agent)-scoped.

| Flag                | Type              | Default | Description                    |
| ------------------- | ----------------- | ------- | ------------------------------ |
| `--agent <agentId>` | `string`          | -       | Scope to a specific agent      |
| `--limit <n>`       | `string`          | `50`    | Maximum observations to return |
| `--format <format>` | `table` \| `json` | `table` | Output format                  |

#### `memory entities`

List an agent's entity graph, most-mentioned first. **Admin-gated** and (tenant, agent)-scoped.

| Flag                | Type              | Default | Description                |
| ------------------- | ----------------- | ------- | -------------------------- |
| `--agent <agentId>` | `string`          | -       | Scope to a specific agent  |
| `--limit <n>`       | `string`          | `100`   | Maximum entities to return |
| `--format <format>` | `table` \| `json` | `table` | Output format              |

#### `memory clear`

| Flag                  | Type      | Default | Description                                        |
| --------------------- | --------- | ------- | -------------------------------------------------- |
| `--filter <filter>`   | `string`  | -       | Filter expression (e.g. `memoryType=conversation`) |
| `--tenant <tenantId>` | `string`  | -       | Filter by tenant ID                                |
| `-y, --yes`           | `boolean` | `false` | Skip confirmation prompt                           |

<Warning>At least one filter (`--filter` or `--tenant`) is required. This safety check prevents accidental blanket clears.</Warning>

#### `memory export`

Export an agent's memory to a versioned, secret-scrubbed JSON file. **Admin-gated.**

```
comis memory export --agent <agentId> [--output <path>] [--limit <n>]
```

The output file uses the `comis-memory-export-v1` envelope format. Every `content` field is
run through the secret scrubber before being written — secrets matching known patterns (API
keys, tokens) are replaced with `[REDACTED]`. The file is written with permissions `0600`
(owner read/write only).

The daemon returns the scrubbed payload over RPC; the CLI writes the file locally. This design
means `memory export` works correctly even when the daemon runs under `node --permission` (which
disables fd-based filesystem APIs on the daemon side).

| Flag                | Type     | Default                                   | Description                             |
| ------------------- | -------- | ----------------------------------------- | --------------------------------------- |
| `--agent <agentId>` | `string` | —                                         | Agent whose memory to export (required) |
| `--output <path>`   | `string` | `comis-memory-<agentId>-<timestamp>.json` | Output file path                        |
| `--limit <n>`       | `string` | `10000`                                   | Maximum entries to export               |

#### `memory import`

Import memory from a `comis-memory-export-v1` JSON envelope into a target agent scope. **Admin-gated.**

```
comis memory import <file> --agent <agentId> [--dry-run]
```

The CLI validates the envelope's `schemaVersion` before sending anything to the daemon.
Files with a mismatched or missing `schemaVersion` are rejected with a non-zero exit code
(fail-closed: no data is sent to the daemon).

Every entry is routed through the memory-poisoning firewall on the daemon:

* **CRITICAL** entries (secret-bearing content) are blocked — not persisted, counted in `blocked`.
* **WARN** entries (jailbreak-pattern content) are stored at downgraded `external` trust with
  a `security-tainted` tag, counted in `downgraded`.
* **Clean** entries are stored at `learned` trust (capped — `system` trust cannot be imported).

Import re-stamps `tenantId` and `agentId` to the target scope. The envelope's `scope` field
is metadata only and is never used for writes.

| Flag                | Type      | Default | Description                                      |
| ------------------- | --------- | ------- | ------------------------------------------------ |
| `--agent <agentId>` | `string`  | —       | Target agent to import into (required)           |
| `--dry-run`         | `boolean` | `false` | Report blocked/downgraded counts without writing |

#### `memory pin <id>`

Mark a memory entry as always-injected in recall — the entry is prepended to every recall result regardless of fused score. **Admin-gated.**

```
comis memory pin <entryId> [--agent <agentId>] [--tenant <tenantId>]
```

Idempotent: pinning an already-pinned entry is a no-op. The entry must exist in the target (tenant, agent) scope.

| Flag                  | Type     | Default | Description  |
| --------------------- | -------- | ------- | ------------ |
| `--agent <agentId>`   | `string` | —       | Agent scope  |
| `--tenant <tenantId>` | `string` | —       | Tenant scope |

#### `memory unpin <id>`

Remove the always-inject pin from a memory entry. **Admin-gated.**

```
comis memory unpin <entryId> [--agent <agentId>] [--tenant <tenantId>]
```

Idempotent: unpinning an already-unpinned entry is a no-op.

| Flag                  | Type     | Default | Description  |
| --------------------- | -------- | ------- | ------------ |
| `--agent <agentId>`   | `string` | —       | Agent scope  |
| `--tenant <tenantId>` | `string` | —       | Tenant scope |

```bash theme={}
# Search memory
comis memory search "project requirements" --limit 5

# View memory statistics (incl. the recall-counter overlay)
comis memory stats

# Inspect a session's recall trace — full ranking breakdown as JSON
comis memory recall-trace my-session --format json
comis memory recall-trace my-session --trace-id 9f2c1a --agent default --limit 50

# List observation provenance for an agent
comis memory observations --agent default --limit 50

# List an agent's entity graph (most-mentioned first)
comis memory entities --agent default --limit 100

# Clear conversation memory for a specific tenant
comis memory clear --tenant default --filter memoryType=conversation --yes

# Export an agent's memory to a file (secrets scrubbed, 0600 permissions)
comis memory export --agent default --output backup.json

# Import from a backup envelope (dry-run first to preview firewall counts)
comis memory import backup.json --agent default --dry-run
comis memory import backup.json --agent default

# Pin a memory entry so it is always injected in recall (admin)
comis memory pin <entryId> --agent default

# Unpin a previously-pinned memory entry (admin)
comis memory unpin <entryId> --agent default
```

<Note>
  Recall-trace records only exist when `diagnostics.recallTrace.enabled` is on (opt-in, default off). See the [recall runbook](/operations/troubleshooting#memory-recall-issues) for the enable-and-read workflow.
</Note>

***

### `comis models`

Model management commands for browsing the model catalog and updating agent model assignments. The catalog is sourced live from the [`@earendil-works/pi-ai`](https://www.npmjs.com/package/@earendil-works/pi-ai) SDK — adding a new provider/model to pi-ai automatically surfaces it here with no Comis update required.

When the daemon is running, the commands fetch via the `models.list` RPC. When the daemon is stopped (e.g., during `comis init`), they fall back to the local pi-ai static catalog so the wizard works pre-init.

#### Subcommands

| Subcommand                   | Description                            |
| ---------------------------- | -------------------------------------- |
| `models list`                | List available models from the catalog |
| `models set <agent> <model>` | Set the model for an agent             |

#### `models list`

| Flag                    | Type              | Default | Description             |
| ----------------------- | ----------------- | ------- | ----------------------- |
| `--provider <provider>` | `string`          | -       | Filter by provider name |
| `--format <format>`     | `table` \| `json` | `table` | Output format           |

#### `models set <agent> <model>`

| Flag                  | Type     | Default       | Description      |
| --------------------- | -------- | ------------- | ---------------- |
| `-c, --config <path>` | `string` | Auto-detected | Config file path |

```bash theme={}
# List all available models (~1,057 entries across 35 providers as of pi-ai 0.80.6)
comis models list

# Filter by provider
comis models list --provider anthropic
comis models list --provider openrouter

# Machine-readable
comis models list --format json

# Set agent model
comis models set my-agent claude-sonnet-4-5-20250929
```

***

### `comis mcp`

Manage Model Context Protocol (MCP) server connections. Connect, disconnect, probe, and authenticate MCP servers; inspect their exposed tools and capabilities.

All subcommands accept `--token <token>` to override `COMIS_GATEWAY_TOKEN`.

#### Subcommands

| Subcommand              | Description                                                                                                                    |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `mcp list`              | List all MCP server connections (name, transport, status, tool count). `--format table\|json`                                  |
| `mcp status <name>`     | Show detailed status for one server: tools, capabilities, serverInfo. `--format table\|json`                                   |
| `mcp test <name>`       | Probe an MCP server configuration without persisting it. Flags: `--transport stdio\|sse\|http`, `--command`, `--args`, `--url` |
| `mcp connect <name>`    | Connect (or re-configure) an MCP server. Flags: `--transport`, `--command`, `--args`, `--url`, `--header`, `--format`          |
| `mcp disconnect <name>` | Disconnect an MCP server                                                                                                       |
| `mcp reconnect <name>`  | Reconnect an MCP server                                                                                                        |
| `mcp login <name>`      | Initiate OAuth device-code flow for an MCP server                                                                              |
| `mcp logout <name>`     | Clear stored OAuth tokens for an MCP server                                                                                    |

```bash theme={}
# List all connected MCP servers
comis mcp list

# Inspect tools and capabilities for a specific server
comis mcp status my-server --format json

# Test a server config without persisting it
comis mcp test my-server --transport stdio --command "npx" --args "-y,my-mcp-server"

# Connect an MCP server
comis mcp connect my-server --transport stdio --command "npx" --args "-y,my-mcp-server"

# Initiate OAuth for an MCP server
comis mcp login my-server

# Disconnect an MCP server
comis mcp disconnect my-server
```

***

### `comis providers`

Provider management commands. Like `comis models`, the provider list is sourced live from the pi-ai SDK catalog (`getProviders()` via the `models.list_providers` RPC), with local fallback when the daemon isn't running.

#### Subcommands

| Subcommand       | Description                                                   |
| ---------------- | ------------------------------------------------------------- |
| `providers list` | List all available providers with model counts and key status |

#### `providers list`

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

The output includes three columns:

* **Provider** — pi-ai provider id (e.g., `anthropic`, `openrouter`, `groq`)
* **Models** — count of models the provider exposes in the catalog
* **Status** — `configured` (canonical env key resolved), `missing key` (provider listed but key not in env), or `keyless` (local providers like ollama / lm-studio that don't need a key)

```bash theme={}
# Default table output
comis providers list

# Machine-readable
comis providers list --format json
# → [{"provider":"anthropic","modelCount":23,"status":"configured"}, ...]
```

To switch an agent to a different provider, use [`comis agent configure`](#comis-agent) with `--provider <id> --model <id>`.

***

### `comis pm2`

Manage the daemon via pm2 as a cross-platform process supervisor (alternative to systemd on macOS and WSL).

#### Subcommands

| Subcommand    | Description                        |
| ------------- | ---------------------------------- |
| `pm2 setup`   | Generate pm2 ecosystem config file |
| `pm2 start`   | Start daemon via pm2               |
| `pm2 stop`    | Stop daemon via pm2                |
| `pm2 restart` | Restart daemon via pm2             |
| `pm2 status`  | Show pm2 process status            |
| `pm2 logs`    | Stream daemon logs via pm2         |

#### `pm2 setup`

| Flag            | Type      | Default | Description                                                                                                          |
| --------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `--enable-boot` | `boolean` | `false` | Also register pm2 with the platform boot manager (launchd on macOS, systemd on Linux) so the daemon starts on reboot |

#### `pm2 logs`

| Flag              | Type     | Default | Description                    |
| ----------------- | -------- | ------- | ------------------------------ |
| `-n, --lines <n>` | `string` | `50`    | Number of recent lines to show |

The other pm2 subcommands (`start`, `stop`, `restart`, `status`) have no additional flags.

```bash theme={}
# One-time setup
comis pm2 setup

# Start and verify
comis pm2 start
comis pm2 status

# Stream logs
comis pm2 logs -n 100
```

***

### `comis reset`

Reset sessions, configuration, or the entire workspace. Requires explicit confirmation for all destructive operations.

| Argument   | Description                                         |
| ---------- | --------------------------------------------------- |
| `<target>` | What to reset: `sessions`, `config`, or `workspace` |

| Flag                  | Type      | Default | Description                                   |
| --------------------- | --------- | ------- | --------------------------------------------- |
| `--yes`               | `boolean` | `false` | Skip confirmation prompt                      |
| `-c, --config <path>` | `string`  | -       | Config file path for resolving data directory |

* **sessions** -- Deletes all conversation sessions (tries RPC first, falls back to direct database removal)
* **config** -- Removes `config.yaml` and `.env` from the config directory
* **workspace** -- Removes the entire data directory and config files

<Warning>The `workspace` target deletes all data including sessions, memory, logs, and configuration. This cannot be undone.</Warning>

```bash theme={}
# Clear all sessions
comis reset sessions --yes

# Full workspace reset
comis reset workspace --yes
```

***

### `comis secrets`

Encrypted secret management using AES-256-GCM. All operations work offline without a running daemon.

#### Subcommands

| Subcommand              | Description                                        |
| ----------------------- | -------------------------------------------------- |
| `secrets init`          | Generate a new master encryption key               |
| `secrets set <name>`    | Encrypt and store a secret                         |
| `secrets get <name>`    | Decrypt and display a secret                       |
| `secrets list`          | List stored secrets (metadata only, no values)     |
| `secrets delete <name>` | Delete a secret from the store                     |
| `secrets import`        | Import secrets from a `.env` file                  |
| `secrets audit`         | Scan config and `.env` files for plaintext secrets |

#### `secrets init`

| Flag      | Type      | Default | Description                                                 |
| --------- | --------- | ------- | ----------------------------------------------------------- |
| `--write` | `boolean` | `false` | Append key to `~/.comis/.env` instead of printing to stdout |

#### `secrets set <name>`

| Flag                    | Type      | Default       | Description                                      |
| ----------------------- | --------- | ------------- | ------------------------------------------------ |
| `--value <value>`       | `string`  | -             | Secret value (alternative to interactive prompt) |
| `--stdin`               | `boolean` | `false`       | Read value from stdin pipe                       |
| `--provider <provider>` | `string`  | Auto-detected | Provider tag for categorization                  |

#### `secrets get <name>`

| Flag        | Type      | Default | Description                                                                                                                                                                                                                                                                                                        |
| ----------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--yes`     | `boolean` | `false` | Skip confirmation prompt                                                                                                                                                                                                                                                                                           |
| `--offline` | `boolean` | `false` | Read the local encrypted store directly (no daemon RPC). Needs `SECRETS_MASTER_KEY` in `~/.comis/.env`. Use when the daemon is down or the gateway token itself is the secret you need (`comis secrets get COMIS_GATEWAY_TOKEN --offline`) — daemon-side reads are audit-logged, so the RPC path stays the default |

#### `secrets list`

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

#### `secrets delete <name>`

| Flag    | Type      | Default | Description              |
| ------- | --------- | ------- | ------------------------ |
| `--yes` | `boolean` | `false` | Skip confirmation prompt |

#### `secrets import`

| Flag            | Type     | Default         | Description             |
| --------------- | -------- | --------------- | ----------------------- |
| `--file <path>` | `string` | `~/.comis/.env` | Source `.env` file path |

#### `secrets audit`

| Flag                  | Type       | Default                                              | Description                                     |
| --------------------- | ---------- | ---------------------------------------------------- | ----------------------------------------------- |
| `--config <paths...>` | `string[]` | `~/.comis/config.yaml`, `~/.comis/config.local.yaml` | Config file paths to scan                       |
| `--env-file <path>`   | `string`   | `~/.comis/.env`                                      | Path to `.env` file                             |
| `--check`             | `boolean`  | `false`                                              | Exit with code 1 if any findings exist (for CI) |
| `--json`              | `boolean`  | `false`                                              | Output findings as JSON array                   |

```bash theme={}
# Generate and save master key
comis secrets init --write

# Store a secret interactively
comis secrets set OPENAI_API_KEY

# Store a secret from a pipe
echo "sk-..." | comis secrets set OPENAI_API_KEY --stdin

# Import secrets from .env file
comis secrets import --file ~/.comis/.env

# Audit for plaintext secrets (CI-friendly)
comis secrets audit --check --json
```

***

### `comis security`

Security audit and auto-remediation tools. Runs all check categories covering configuration validation, file permissions, secret exposure, gateway hardening, and more.

#### Subcommands

| Subcommand           | Description                                                        |
| -------------------- | ------------------------------------------------------------------ |
| `security audit`     | Run security audit checks                                          |
| `security fix`       | Auto-remediate security findings (dry-run by default)              |
| `security audit-log` | Query the durable security-decision audit log (`obs_audit_events`) |

#### `security audit`

| Flag                      | Type                              | Default | Description                |
| ------------------------- | --------------------------------- | ------- | -------------------------- |
| `-c, --config <paths...>` | `string[]`                        | -       | Config file paths to audit |
| `--format <format>`       | `table` \| `json`                 | `table` | Output format              |
| `--severity <level>`      | `info` \| `warning` \| `critical` | `info`  | Minimum severity to show   |

#### `security fix`

| Flag                      | Type              | Default | Description                                                   |
| ------------------------- | ----------------- | ------- | ------------------------------------------------------------- |
| `--yes`                   | `boolean`         | `false` | Apply fixes without confirmation (default is dry-run preview) |
| `-c, --config <paths...>` | `string[]`        | -       | Config file paths                                             |
| `--format <format>`       | `table` \| `json` | `table` | Output format                                                 |

#### `security audit-log`

Query the **durable** security-decision audit log — the `obs_audit_events` SQLite
table populated by the daemon (secret access, injection detection, command blocks,
canary leaks, sandbox-downgrade refusals, and classified `audit:event` actions).
Admin-scoped (the operator gateway token carries admin scope); rows are
**content-free** (ids, closed-enum fields, a scrubbed `refs` blob — never a secret
value). Reads the same events surfaced by the `obs.audit.query` RPC and the
`obs_query {action:"audit"}` agent tool.

| Flag                       | Type                                | Default | Description                                                         |
| -------------------------- | ----------------------------------- | ------- | ------------------------------------------------------------------- |
| `--kind <kind>`            | `string`                            | -       | Filter by event family (e.g. `secret_access`, `injection_detected`) |
| `--classification <class>` | `read` \| `mutate` \| `destructive` | -       | Filter by risk class                                                |
| `--agent <id>`             | `string`                            | -       | Filter by agent id                                                  |
| `--tenant <tenant>`        | `string`                            | -       | Filter by tenant scope (`""` matches system-scoped events)          |
| `--outcome <outcome>`      | `success` \| `failure` \| `denied`  | -       | Filter by outcome                                                   |
| `--since <ms>`             | `number`                            | -       | Lower time bound (inclusive epoch ms)                               |
| `--until <ms>`             | `number`                            | -       | Upper time bound (inclusive epoch ms)                               |
| `--limit <n>`              | `number`                            | `200`   | Max rows (clamped to 1000)                                          |
| `--format <format>`        | `table` \| `json`                   | `table` | Output format                                                       |

```bash theme={}
# Run security audit
comis security audit -c ~/.comis/config.yaml

# Show only critical findings
comis security audit --severity critical

# Preview fixes (dry-run)
comis security fix -c ~/.comis/config.yaml

# Apply fixes
comis security fix --yes -c ~/.comis/config.yaml

# Query the durable audit: a specific agent's secret accesses
comis security audit-log --kind secret_access --agent customer-support --limit 20

# All denied actions in the last hour, as JSON
comis security audit-log --outcome denied --since "$(( $(date +%s000) - 3600000 ))" --format json
```

***

### `comis sessions`

Session management commands for listing, inspecting, and deleting conversation sessions.

#### Subcommands

| Subcommand                    | Description                                                                            |
| ----------------------------- | -------------------------------------------------------------------------------------- |
| `sessions list`               | List all sessions                                                                      |
| `sessions inspect <key>`      | Display full details of a session                                                      |
| `sessions delete <key>`       | Delete a session                                                                       |
| `sessions reset <sessionKey>` | Reset a conversation to a clean slate: clears LCD history + working session transcript |
| `sessions backup`             | Create a timestamped backup of `memory.db`                                             |

#### `sessions list`

| Flag                  | Type              | Default | Description         |
| --------------------- | ----------------- | ------- | ------------------- |
| `--tenant <tenantId>` | `string`          | -       | Filter by tenant ID |
| `--format <format>`   | `table` \| `json` | `table` | Output format       |

#### `sessions inspect <key>`

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

#### `sessions delete <key>`

| Flag    | Type      | Default | Description              |
| ------- | --------- | ------- | ------------------------ |
| `--yes` | `boolean` | `false` | Skip confirmation prompt |

#### `sessions reset <sessionKey>`

Complete conversation reset — clears **both** the LCD durable history **and** the daemon sessionStore working transcript. **This operation is irreversible.** Admin-gated — requires an admin token. After a reset, the model starts with a clean slate for this session in **both dag and pipeline modes**: the LCD store is empty (dag mode) and the sessionStore messages are empty (both modes — the JSONL-backed transcript that feeds `state.messages` on the next turn is wiped).

This is the correct command when you want the model to explicitly forget a conversation. Deleting the session JSONL file alone (`sessions delete`) does **not** clear the LCD store, so the model would continue with its LCD history intact.

| Flag              | Type      | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ----------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--memory`        | `boolean` | `false` | Also clear the conversation's RAG memories — the full-forget path. Deletes every memory row matching this session (both paired-conversation memories **and** LCD-distilled episodic memories) for the agent + tenant scope, then unlinks them from consolidated observations (an observation built only from this session is deleted; one corroborated by other sessions is kept with the reference removed). The CLI prints the deleted count. If the daemon has no memory store wired, the flag is reported as unavailable (a stderr warning) and only LCD history + the session transcript are cleared. |
| `--purge-derived` | `boolean` | `false` | Only meaningful with `--memory`. Escalates to deleting **every** consolidated observation derived from this session — even those still corroborated by other sessions (nuclear forget). Destructive; use only when a complete erasure of anything learned from the session is required.                                                                                                                                                                                                                                                                                                                    |
| `--yes`           | `boolean` | `false` | Required — skip the confirmation prompt (the command exits with an error if omitted)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |

<Warning>
  Use `sessions reset` (not `sessions delete`) when you want the model to explicitly forget a conversation. Deletion only removes the session record; the LCD store keeps its full history and the model would resume from it. See [Data Directory](/operations/data-directory) for the full durability model.
</Warning>

<Note>
  `--memory` is the explicit **delete** side of the LCD→LTM loop. There is also an automatic, **non-deleting** complement on the recall side: when a conversation's condensed LCD summary has been distilled into a learned memory, recall **down-weights** (it does not remove) the overlapping paired-conversation memories from that same session, so the distilled summary and its own source rows do not double-count. That down-weighting needs no flag and never deletes anything — `--memory` / `--purge-derived` remain the only commands that actually remove memories.
</Note>

```bash theme={}
# List all sessions
comis sessions list

# Inspect a specific session
comis sessions inspect "default:user123:telegram"

# Delete a session
comis sessions delete "default:user123:telegram" --yes

# Full conversation reset (clears LCD history + session transcript — irreversible, requires --yes)
comis sessions reset "default:user123:telegram" --yes

# Also clear RAG memories for this session (the full-forget path — prints deleted count)
comis sessions reset "default:user123:telegram" --memory --yes

# Nuclear forget — also purge consolidated observations derived from this session
comis sessions reset "default:user123:telegram" --memory --purge-derived --yes
```

#### `sessions backup`

Creates a timestamped copy of `memory.db` using the SQLite Online Backup API. Safe to run while
the daemon is running — the backup API handles concurrent writes atomically. The backup file is
placed in the same directory as `memory.db` with restricted permissions (`0600`).

| Flag               | Type     | Default    | Description                 |
| ------------------ | -------- | ---------- | --------------------------- |
| `--data-dir <dir>` | `string` | `~/.comis` | Override the data directory |

```bash theme={}
# Back up memory.db (safe while daemon is running)
comis sessions backup

# Back up from a custom data directory
comis sessions backup --data-dir /var/comis
```

The backup filename includes an ISO timestamp: `memory.db.backup.20260610T120000000Z`.

<Note>
  Run `comis sessions backup` before schema migrations or destructive operations. To restore,
  copy the backup file back to `memory.db` while the daemon is stopped.
</Note>

***

### `comis signal-setup`

Interactive setup wizard for installing and configuring Signal CLI. Guides through Java prerequisite checks, signal-cli installation from GitHub releases, phone number registration with SMS or voice verification, and a test message.

No flags. Requires an interactive terminal (TTY).

```bash theme={}
# Launch Signal setup wizard
comis signal-setup
```

***

### `comis skills`

Manage prompt skills. Today the group has one subcommand: `import`.

`comis skills import <ref>` installs a skill from a GitHub directory URL, a packaged archive, an allowlisted well-known registry, or ClawHub through the daemon's staged pipeline. The import is scanned and MCP-checked **before** anything is written, and a successful import is stamped the `imported` trust tier and pinned in the provenance store. See [Importing Skills](/skills/importing) for the full model.

Accepts `--token <token>` to override `COMIS_GATEWAY_TOKEN`; the token is resolved before the socket opens, so a missing token surfaces a friendly error naming the env var rather than a generic handshake failure.

| Subcommand            | Description                                                                                                                                                                      |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `skills import <ref>` | Import a skill. `<ref>` is a GitHub directory URL (default), an archive URL (`--source archive`), a skill name (`--source wellknown`), or an `@owner/slug` (`--source clawhub`). |

| Flag                  | Values                                            | Description                                                                                                                                                                                                                                                                                    |
| --------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--source <source>`   | `github` \| `archive` \| `wellknown` \| `clawhub` | Acquisition channel. `github` (default) treats `<ref>` as a directory URL; `archive` treats it as a `.skill`/`.zip`/`.tar` URL; `wellknown` resolves `<ref>` as a skill **name** from the `--registry` origin's well-known index; `clawhub` resolves `<ref>` as an `@owner/slug` from ClawHub. |
| `--registry <origin>` | `https://host[:port]`                             | Registry origin to resolve `<ref>` from -- required with `--source wellknown`. Must be allowlisted in [`skills.import.registries`](/reference/config-yaml#skills-agents-skills); a non-allowlisted registry refuses flatly.                                                                    |
| `--scope <scope>`     | `local` \| `shared`                               | `local` (default) installs into this agent's workspace; `shared` installs into the global directory (default agent only).                                                                                                                                                                      |
| `--confirm`           | flag                                              | Confirm a re-import whose content diverges from the pinned hash of a prior import of the **same** source. Never overrides a name collision on an unprovenanced or foreign-source skill -- delete that skill first. There is no `force` flag.                                                   |
| `--format <format>`   | `table` \| `json`                                 | Output format (default `table`).                                                                                                                                                                                                                                                               |
| `--token <token>`     | string                                            | Gateway token, overriding `COMIS_GATEWAY_TOKEN`. Prefer the env var or `~/.comis/.env` -- a token on the command line is visible via `ps`/`proc` and shell history.                                                                                                                            |

```bash theme={}
# Import from a GitHub directory (source defaults to github)
comis skills import https://github.com/owner/repo/tree/main/skills/my-skill

# Import a packaged archive into the shared directory
comis skills import https://example.com/my-skill.skill --source archive --scope shared

# Import by name from an allowlisted well-known registry
comis skills import my-skill --source wellknown --registry https://registry.example

# Import an @owner/slug skill from ClawHub (allowlist the `clawhub` token first)
comis skills import @acme/pdf-extractor --source clawhub

# Re-import the same source after its content changed
comis skills import https://example.com/my-skill.skill --source archive --confirm
```

On success the command reports the installed name, path, kept-file count, the `imported` source, and the resolved agent id.

***

### `comis trace`

Search, tail, and export agent execution traces. Traces capture the inbound message pipeline and are stored in the durable trace store. Backed by `ObsTraceSearchContract`, `ObsTraceTailContract`, and `ObsTraceExportContract`.

Primarily flag-driven; use `--chat` with `--tail` to stream live events.

#### Flags

| Flag                  | Description                                         |
| --------------------- | --------------------------------------------------- |
| `--message-id <uuid>` | Search trace rows by inbound message ID             |
| `--trace-id <uuid>`   | Search trace rows by trace ID                       |
| `--chat <chatId>`     | Filter or tail trace events by chat ID              |
| `--tail`              | Stream events live (requires `--chat`; polls \~1 s) |
| `--since <duration>`  | Time window, e.g., `10m`, `1h`                      |
| `--where <filter>`    | Filter expression, e.g., `error`                    |
| `--json`              | Machine-readable JSON output                        |

#### Subcommands

| Subcommand                 | Description                                                                             |
| -------------------------- | --------------------------------------------------------------------------------------- |
| `trace export <sessionId>` | Export a session bundle and print the output path. `--json` for machine-readable output |

```bash theme={}
# Search traces by message ID
comis trace --message-id 550e8400-e29b-41d4-a716-446655440000

# Search traces by trace ID
comis trace --trace-id 6ba7b810-9dad-11d1-80b4-00c04fd430c8

# Tail live trace events for a chat
comis trace --chat my-chat-id --tail

# Search recent errors
comis trace --since 1h --where error --json

# Export a session bundle
comis trace export my-session-id
```

***

### `comis cost export`

Export the corrected-cost time buckets as CSV (default) or JSON, honoring agent / provider / model / time-window filters. Each row carries the cost rollup — `totalCost`, `totalTokens`, `callCount`, `totalCacheSaved`, `totalCostCorrection` (the SDK-vs-corrected adjustment) — **plus a pricing-coverage column** so a finance review sees how trustworthy the dollars are: `pricingState` (the bucket's dominant three-state signal — `priced` / `free` / `unknown`) and `missingPricingCount` (how many calls in the bucket had an `unknown` or unset pricing state, i.e. dollars **not** catalog-backed). The export is **content-free** — ids, counts, dollars, and the pricing enum only, never a message body, secret, or query.

Buckets are **hourly** by default; `--quarter-hour` switches to 15-minute buckets (the four quarter-hour buckets inside an hour always sum back to that hour's total).

The command reads the **local `~/.comis` observability store directly** (the telemetry lives on disk) — it contacts no daemon and needs no gateway token. A missing or unreadable store yields an empty export (an honest empty, never a fabricated zero-cost row).

| Flag                | Type            | Default  | Description                                                                           |
| ------------------- | --------------- | -------- | ------------------------------------------------------------------------------------- |
| `--format <format>` | `csv` \| `json` | `csv`    | Output format                                                                         |
| `--quarter-hour`    | `boolean`       | `false`  | Bucket by 15-minute quarter-hours instead of hours                                    |
| `--since <ms>`      | `number`        | all time | Lower time bound (epoch ms); a non-numeric value is ignored (widen rather than crash) |
| `--agent <id>`      | `string`        | —        | Filter to one agent                                                                   |
| `--provider <id>`   | `string`        | —        | Filter to one provider                                                                |
| `--model <id>`      | `string`        | —        | Filter to one model                                                                   |

```bash theme={}
# Export all corrected-cost hourly buckets as CSV to a file
comis cost export > cost.csv

# Last 24h, quarter-hour granularity, one agent, as JSON
comis cost export --quarter-hour --since "$(( $(date +%s000) - 86400000 ))" --agent customer-support --format json

# One provider+model slice
comis cost export --provider openai --model gpt-4o-mini
```

***

### `comis explain`

Assemble an `IncidentReport` for a single agent session — a bounded, causal post-mortem (outcome, cost, per-tool stats, normalized failures, circuit-breaker timeline, large-result offloads, recall health — including degraded/failed recall lanes — session-wide activity-finalize tallies, and a deterministic likely root cause). The report is derived from log evidence only; no LLM is invoked, so the same session always yields the same verdict. Backed by `ObsExplainContract` (`obs.explain`).

The positional argument is routed automatically by shape: a **`root-` run id** (an autonomy run's `rootRunId` — the synthetic in-process `root-session-<sessionKey>` or a real spawned-run lease id; checked **first** because a synthetic root contains `:` but must route as a run id, not a session key) is passed as `rootRunId`; otherwise a **session key** (`tenant:user:channel:ts`, contains `:`) is passed as `sessionKey`; otherwise a **trace ID** (a UUID, no `:`) is passed as `traceId`. The daemon canonicalizes a trace ID *or* a `rootRunId` to its session key first, so all three forms produce the identical report. A `rootRunId` is the **`comis fleet` → `comis explain` drill-down target**: paste the `worstRootRunId` that [`comis fleet`](#comis-fleet)'s autonomy block names straight in, and the report renders that run's `spawnTree` (an unresolvable id surfaces an honest `session_not_found`, never a clean-looking empty report).

When the session included a transcription or synthesis, the report carries a `voice` block reconstructing that turn: the `provider`, whether it ran `keyless`, the `model`, `durationMs`, the `outcome`, the resolved selection `source` rung (with the `onSkip` reasons when `auto` skipped a higher rung), the `costUsd` (`0` for a keyless turn; omitted for a keyed turn — the audio ports return no per-call cost), and the failure `errorKind`. Content-free — ids, labels, and numbers only, never the audio or transcript text. See [Voice → Observability and cost](/media/voice#observability-and-cost).

When the session made capability-gated calls (an autonomous agent, or one that spawned children via `orchestrate`), the report carries a `spawnTree` block reconstructing the run's authorization topology — the **one call to root-cause an unattended run**. Each node is a `leaseId` (the in-process root groups under its synthetic `rootRunId`) and surfaces its `parentLeaseId` (the child→root edge; absent on the root), the `agentId`, the attenuated `caps` it held, the tool NAMES it invoked (`toolsInvoked`), and any capability it was DENIED (`denials` — each a `CapabilityDeniedError` cap). In the table view a node renders as `<leaseId> ←<parent>  caps=[…] tools=[…] DENIED=[…]`; `--format json` emits the full `spawnTree` array. The tree is reconstructed offline from the session's `capability.audited` trajectory records, so `--offline` shows it too. **Multi-agent graph (DAG) nodes appear as leaves too**: a graph node spawns in-process and never crosses the socket chokepoint that emits `capability.audited`, so each node also emits a `graph.node_spawned` record — reconstructed as a leaf keyed by `<graphId>:<nodeId>` (its stable node identity, not a socket lease), nested under the graph root via `parentLeaseId`, carrying its child `agentId` and `caps:["orch:graph"]` (without this the spawn-tree showed only the root while N graph children ran). Content-free — ids, caps, tool NAMES, and decision-derived denials only, never a tool argument, a message body, or a secret. Per-node remaining **budget** is *not* on the offline tree (it is live daemon state) — use `comis whoami` for an in-flight run's remaining budget; the spawn tree is the post-mortem topology.

When a scheduled job's [wake-gate](/agent-tools/scheduling#wake-gate-run-the-model-only-when-it-matters) **woke** the model, the report folds a content-free `cronWakeGate` fact for that fire — the job id, the `wake` verdict, the gate's `durationMs`, its tool-call count, and the model turns it saved (ids and counts only, never the gate's gathered finding). A **skipped** fire is different: it runs no model and opens no session, so it never reaches `comis explain`. The argument is a session key, trace ID, or `rootRunId` — **never a cron job id**. To inspect a job's skipped fires read its run history (`cron.runs`), and for the cross-job skip-rate and turns-saved use [`comis fleet`](#comis-fleet).

| Flag                | Type                | Default   | Description                                                                                                                                                                                      |
| ------------------- | ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--format <format>` | `table` \| `json`   | `table`   | Output format                                                                                                                                                                                    |
| `--depth <depth>`   | `summary` \| `full` | `summary` | `summary` bounds the report to roughly 6 KB (about 1,500 tokens) and caps the failure list; `full` relaxes the array caps. Both depths are digest-only — no raw tool-output body is ever inlined |
| `--offline`         | `boolean`           | `false`   | Assemble from the local `~/.comis` files without contacting the daemon (same pure assembler the RPC handler runs)                                                                                |

```bash theme={}
# Explain a session by session key
comis explain "default:user123:telegram:1717000000"

# Explain by trace ID, with the uncapped (full) report
comis explain 6ba7b810-9dad-11d1-80b4-00c04fd430c8 --depth full

# Drill into an autonomy run by its rootRunId (the `comis fleet` worstRootRunId) —
# resolves the run to its session and renders the run's spawn-tree
comis explain "root-session-default:user123:telegram:1717000000"

# Machine-readable output (includes the spawnTree array for an autonomous run)
comis explain "default:user123:telegram:1717000000" --format json

# No daemon required — assemble from the local files
comis explain "default:user123:telegram:1717000000" --offline
```

Requires an admin-scoped gateway token (same as `comis trace`) — unless `--offline`. When the gateway is **unreachable** (daemon down / wrong port) the command automatically falls back to the offline assembly with a notice. When the gateway **rejects the token** it does NOT silently fall back — the daemon is demonstrably running, so the error names `COMIS_GATEWAY_TOKEN` (env var or `~/.comis/.env`) and suggests `--offline` as the explicit out.

***

### `comis orchestrate`

Operator-only deterministic replay of an [`orchestrate`](/agent-tools/orchestrate) run. Available only when [`autonomy.durability.orchestrateResume`](/reference/config-yaml#autonomy-agentsautonomy) was on for the run (so the pinned script + its recorded results survived).

| Subcommand                   | Description                                                                                       |
| ---------------------------- | ------------------------------------------------------------------------------------------------- |
| `orchestrate replay <runId>` | Deterministically re-run a durable run's **pinned** script bytes and print its stdout — **admin** |

```
comis orchestrate replay <runId>
```

`replay` re-runs the run's **pinned** `<runId>.<language>` script bytes against a **separate, operator-invoked replay socket** that serves the run's recorded cap-call results (from `results/replay.jsonl`) back in order — so the same pinned script + recorded responses produce **byte-identical stdout**. The caller supplies only the `runId`; **no script bytes are ever accepted** (the pinned bytes are the sole source). The replay socket is a physically separate Unix socket that speaks the capability wire but can only serve recorded results — it is **never** a mode of the production capability endpoint, so a replay cannot perform a live side effect. Backed by `OrchestrateReplayContract` (`orchestrate.replay`).

| Flag                | Type             | Default | Description                                                                                     |
| ------------------- | ---------------- | ------- | ----------------------------------------------------------------------------------------------- |
| `--format <format>` | `text` \| `json` | `text`  | `text` writes the recorded stdout verbatim; `json` emits `{ "stdout": "…", "diverged": false }` |

```bash theme={}
# Re-run a durable run's pinned bytes → the recorded, byte-identical stdout
comis orchestrate replay orch-abc123

# Machine-readable — the stdout plus a divergence flag
comis orchestrate replay orch-abc123 --format json
```

`orchestrate.replay` is **admin-scoped and deny-by-origin**: an agent-origin call is rejected before the handler runs (an agent can never replay a run), and it requires an admin-scoped gateway token. An unknown `runId`, or a run with no pinned script (durability was off, or its checkpoint was reclaimed), returns a content-free error naming the failure class — never the `runId`.

***

### `comis fleet`

Assemble a `FleetHealthReport` — a bounded, **cross-session** triage over a recent window: the session count and degradation rate, the **degraded-by-cause breakdown** (degraded sessions bucketed by named `endReason` cause, e.g. `context_exhausted` / `output_starved`), the recurring `WARN` findings (recurring `lcd_divergence` / model / config-posture signals, counts + hints only), the total circuit-breaker trips, the window cost, and a deterministic likely root cause. Like `comis explain`, the report is derived from log + diagnostics evidence only; no LLM is invoked, so the same window yields the same verdict. Backed by `ObsFleetHealthContract` (`obs.fleet.health`).

`cost.costUsd` is the per-session spend (the session-summary rollup). `cost.offSessionUsd` is the **background-job** spend in the window — reflection/learning cron runs et al. that key their token usage to a synthetic `__PREFIX__` session with no session\_summary, so their cost is **absent** from `costUsd`. The two are distinct and never double-count: the operator's full provider bill is `costUsd + offSessionUsd`. The table view appends `+ $Z off-session (reflection/background)` only when it is non-zero. This closes a reconciliation gap where reflection spend hit the provider console but was invisible to the fleet lens.

The table view prints the degraded-by-cause spread as a `Degraded by cause: context_exhausted=13, output_starved=9` line (sorted highest-count first; counts + closed-set labels only, no raw bodies) — omitted when no degraded session carried a named cause. The full per-cause map is also on the `--format json` output (`degradedByCause`).

A `health_signal:cache_prefix_churn` finding fires when a session's Anthropic prompt-cache prefix **collapsed** on a recurring basis — a cached-region message mutated across turns on 3+ calls within a recent window, wasting the cache write (up to hundreds of thousands of cache-creation tokens over a session). The finding names the recurring **mutation class** (`structural-shift` = a message index shifted below the cache fence, e.g. from LCD condense/re-admit; `datetime-preamble` / `thinking-cleared` / `content-cleared` = an in-place edit). This closes a fleet-blindness gap where the churn was visible only as a daemon.log `Unstable prefix detected` WARN. Counts + the closed class label only — never message text.

A dedicated `config_posture:served_below_configured` finding fires when an Ollama provider serves a smaller `num_ctx` than the configured `contextWindow` — the agents on that provider silently run with the smaller window. The count is the number of providers affected at the **latest** boot (posture is standing state, not cumulative — a healthy restart clears it). Fix by raising the served window (`OLLAMA_CONTEXT_LENGTH=<configured> ollama serve`, or Modelfile `PARAMETER num_ctx <configured>` — see the [served-window section](/reference/config-yaml#local-model-context-window) for the VRAM caveat), then run `comis explain` on a served-bound session for the numbers-backed budget verdict — and see the [Local models playbook](/operations/local-models) for the full local-deployment triage map.

A dedicated `config_posture:media_credential_gap` finding fires when a configured media pipeline (`imageGeneration` / `transcription` / `tts` / `videoGeneration`) has a **pinned** provider whose credential is absent — the pipeline authenticates fine for the main completion path but fails at first media use, so it was previously invisible to the fleet lens (you had to hit it or grep the log). The count is the number of affected pipelines at the **latest** boot (standing state; a login/key + restart clears it). Fix by setting the provider's credential (`OPENAI_API_KEY` / `GOOGLE_API_KEY` / `FAL_KEY` / …), logging in for `openai-codex` (`comis auth login --provider openai-codex`), or switching the pipeline's `provider` to one whose credential is present (or `auto` to follow the main provider). Counts only — never a provider name or credential value.

A dedicated `config_posture:unresolved_model` finding fires when one or more agents are configured with a `model` id that does **not** resolve in the model catalog for its provider (and is not an operator-declared custom model under `providers.entries.<provider>.models`) — e.g. `openai-codex: gpt-5.6` when the real ids are `gpt-5.6-terra` / `-luna` / `-sol`. An unresolved model collapses the whole model profile to the **fail-closed nano profile** (an \~8K-token window), so every non-trivial turn context-exhausts — and neither the `chimeric_model` nor the `pricing_gap` finding catches it (a non-native provider resolves `"free"` for an unknown model, and the model family still parses). The count is the number of affected agents at the **latest** boot (standing state; a valid model id + restart clears it). Fix by setting `agents.<id>.model` to a valid catalog id for its provider — the `daemon.log` unresolved-model WARN names the provider's available ids — or declare the model under `providers.entries.<provider>.models`. Counts only — never a model id in the finding body.

A `voice_health` finding fires when transcriptions/syntheses degraded across the window: the count of degraded STT/TTS turns and the dominant voice `errorKind` (e.g. `model_load_failed` → check the local whisper model cache, `auth_required` → set the provider's audio key). Counts + a closed errorKind label + a static hint only — no raw provider body or secret, safe to paste. Drill into the worst voice session it points at with `comis explain`. See [Voice → Observability and cost](/media/voice#observability-and-cost).

The acute-degradation root cause (`fleet_acute_degradation`) names the **worst degraded session's key** inline (`worst: <sessionKey>`) and in its suggested next step (`comis explain <sessionKey>`), so you paste it straight in rather than hunting for "the worst session".

When the daemon runs **unattended/durable** runs, the report carries an `autonomy` block: the cross-run run count + degraded rate (sourced from the **crash-surviving** `durable_runs` table, so it survives a hard crash), the `orphaned` / `resumed` / `revoked` / `killed` counts (`killed` is separable from `revoked` because a hard `run.kill` and a cooperative `lease.revoke` emit distinct events), the autonomy breaker-trip + budget-breach counts, the window cost, and the worst degraded run's `worstRootRunId` (orphaned > killed > revoked) so you can paste it straight into `comis explain` for its spawn-tree. The orphaned/revoked/killed counts also surface as dedicated `durable_orphaned` / `autonomy_revoked` / `autonomy_killed` findings, and a degraded autonomy run drives the top-ranked `fleet_autonomy_degradation` root cause. Counts + the worst run's id only — never a lease bearer, an orphan-reason body, or a secret; safe to paste. The block is **absent** under `--offline` (the daemon-less CLI has no durable-store edge) or on a non-durability boot — an honest coverage degradation, not a silent zero.

When cron jobs ran a [wake-gate](/agent-tools/scheduling#wake-gate-run-the-model-only-when-it-matters) over the window, the report carries a `cronWakeGate` efficiency block and a `cron_wake_gate_efficiency` finding: the gated-fire total, the skip count and **skip-rate**, the **fail-open count and rate**, the model **turns saved**, and the gate's own **tool-call cost**, rolled up per agent (counts and agent ids only — never the gate's gathered findings or script, safe to paste). A high skip-rate is usually the gate *working* (most polls found nothing), not a fault; the three signals worth inspecting are a **100% skip-rate on a monitor you expect to fire**, a **high fail-open rate** (the gate crashed/timed-out/over-capped every fire — it saves nothing and costs its own run, yet reads `skipRate 0` like a busy monitor), and **tool calls exceeding turns saved** (a gate that costs more than it saves). Because a skip opens no session, this is the cross-job lens for it — pair it with `cron.runs jobName "<job>"` for a single job's per-fire decisions.

This is the cross-session **sibling** of `comis explain` (single-session). It is a **remote admin RPC** over the operator's gateway token — DISTINCT from `comis health`, the **local daemon doctor** that runs offline checks with no RPC. (`comis health` is unaffected; this is a separate command.)

| Flag                | Type              | Default | Description                                                                                                                                                                                  |
| ------------------- | ----------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--since <hours>`   | `number`          | `24`    | Aggregation window in hours                                                                                                                                                                  |
| `--format <format>` | `table` \| `json` | `table` | Output format                                                                                                                                                                                |
| `--offline`         | `boolean`         | `false` | Assemble from the local `~/.comis` files without contacting the daemon. Without `memory.db` access the session-summary half degrades and `coverage.sessionSummary.found` reports it honestly |

```bash theme={}
# Fleet-health triage over the last 24h (default window)
comis fleet
#   …
#   Breaker:    3 trips
#   Autonomy:   20 run(s) (3 degraded, 15%) · orphaned=2 resumed=4 revoked=1 killed=0 · breaker=1 budgetBreaches=0
#     → worst run: comis explain root-2f9c1a
#   …
# (the Autonomy line + the worst-run drill-down appear only when the daemon ran
#  durable/unattended runs; absent under --offline / a non-durability boot)

# Widen the window to 72h
comis fleet --since 72

# Machine-readable output
comis fleet --since 24 --format json

# No daemon required — assemble from the local files
comis fleet --since 24 --offline
```

Requires an admin-scoped gateway token (same as `comis explain` / `comis trace`) — unless `--offline`. Unreachable-gateway calls fall back to the offline assembly automatically; a token rejection surfaces the auth error (with the `--offline` tip) instead of masking the misconfiguration.

***

### `comis messages`

Extract the **inbound messages users typed**, per channel, from the local session logs — the conversation-export lens. It reads the raw session `.jsonl` message logs under every agent workspace tree (`<dataDir>/workspace[-<agentId>]/sessions/...`) and parses each user turn's inbound envelope (`[<channelType>] <senderId> (<time>):`), so one command answers "what did users send us on Telegram yesterday" without hand-joining session files.

The command runs **fully offline and has no RPC sibling — on purpose**. Unlike `comis explain` / `comis fleet` (bounded digests, counts + hints only), its output is **content-bearing**: message bodies are the payload. The obs network surfaces stay content-free, so this read never crosses the gateway — it only reads local files the operator already owns.

Internal dispatch turns are excluded by default and **counted, not hidden**: cron, sub-agent, and heartbeat sessions plus the reserved `system` sender are agent-/scheduler-authored prompts, not humans — a summary note reports how many were excluded, and `--include-internal` shows them tagged `origin: "internal"`. Two more honest-coverage notes: user turns with **no parsable envelope** (e.g. `envelope.showProvider: false` produces headerless turns) are counted instead of silently dropped, and when `--limit` cuts the result the **latest N** are kept and the truncation is announced.

`<when>` values for `--since` / `--until` accept **epoch ms** (`1783900800000`), **relative-ago** (`30m`, `24h`, `7d`), or an **ISO date/datetime** (`2026-07-12`, `2026-07-12T10:00:00Z`). An unparsable bound is an **error naming the accepted forms** — never a silent widen-to-everything. `--date` is one-UTC-day sugar and cannot be combined with `--since`/`--until`.

| Flag                  | Type                                   | Default | Description                                                                                         |
| --------------------- | -------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| `--channel <type>`    | `string`                               | —       | Filter by channel type (`telegram`, `discord`, ...)                                                 |
| `--chat <id>`         | `string`                               | —       | Filter by chat/conversation id                                                                      |
| `--sender <id>`       | `string`                               | —       | Filter by sender id                                                                                 |
| `--agent <id>`        | `string`                               | —       | Filter by agent id (`default`, or the `workspace-<agentId>` suffix)                                 |
| `--since <when>`      | `string`                               | —       | Lower bound (inclusive)                                                                             |
| `--until <when>`      | `string`                               | —       | Upper bound (exclusive)                                                                             |
| `--date <YYYY-MM-DD>` | `string`                               | —       | One UTC day (sugar for `--since <day> --until <next day>`)                                          |
| `--limit <n>`         | `number`                               | `500`   | Max messages returned (the latest N are kept)                                                       |
| `--include-internal`  | `boolean`                              | `false` | Include cron/sub-agent/heartbeat/`system` dispatch messages                                         |
| `--format <format>`   | `table` \| `text` \| `json` \| `jsonl` | `table` | `table` previews the first line; `text` is the full chat log; `json`/`jsonl` carry the full records |

```bash theme={}
# Everything users sent, newest window last (first-line preview table)
comis messages

# All Telegram messages from one chat on a specific day, as a readable chat log
comis messages --channel telegram --chat 5177964605 --date 2026-07-12 --format text

# The last 24h across every channel, machine-readable (one JSON record per line)
comis messages --since 24h --format jsonl > messages.jsonl

# A precise time frame
comis messages --since 2026-07-12T10:00:00Z --until 2026-07-12T18:00:00Z
```

Reads `COMIS_DATA_DIR` (falling back to `~/.comis`) like the other offline commands — run it as the daemon's user, or point `COMIS_DATA_DIR` at the daemon's data dir.

***

### `comis support-bundle`

Assemble a **paste-ready support bundle** — a single directory to hand to a maintainer or attach to a bug report. Each run writes up to nine content-free files (plus a `trace-exports/` directory with `--deep`) under `<dataDir>/support-bundles/comis-support-<ts>/`: a machine-readable triage verdict, the full health-check findings, a human-readable issue summary, an AI-fillable issue draft, a cross-session fleet health digest, a config-posture digest (config section membership only), a window-scoped audit-summary digest, and a manifest carrying the redaction fingerprint. Focusing on one session with `--session` adds a per-session `explain.json` incident digest; `--deep` additionally embeds that session's redacted trace bundle under `trace-exports/`. The fleet digest, config-posture digest, and audit-summary digest are each omitted when their source cannot be read — a thrown fleet assembly, an unparseable config, or an absent observability store — and the manifest records the gap. The verdict is derived from the local health checks only — no LLM is invoked, so the same install state always yields the same verdict.

The command runs **fully offline**: it composes the same checks as `comis doctor` against a stopped daemon, folds them into the triage verdict, and writes the bundle without contacting the gateway. A **degraded or partial** triage is still a written bundle — the degraded verdict is the content, not a failure — so the command exits `0` on any bundle it manages to write. It is safe to run the moment something looks wrong.

The bundle is content-free by construction: it excludes secrets, message bodies, and raw config values, and the directory name carries a timestamp only (no hostname). Every file is written under an owner-only (`0o700`) directory with `0o600` file modes through a symlink-refusing safe writer.

| Flag                              | Type              | Default | Description                                                                                                                   |
| --------------------------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `--since <hours>`                 | `number`          | `24`    | Diagnostic window in hours (same unit as `comis fleet`)                                                                       |
| `--format <format>`               | `table` \| `json` | `table` | Output format                                                                                                                 |
| `-c, --config <paths...>`         | `string[]`        | auto    | Config file paths; defaults to `COMIS_CONFIG_PATHS` or the standard `~/.comis` / `/etc/comis` locations                       |
| `--session <sessionKeyOrTraceId>` | `string`          | —       | Focus the bundle on one session — embeds its `explain.json` digest (valid on its own; add `--deep` for the full trace bundle) |
| `--deep`                          | `boolean`         | `false` | Include deep per-session evidence (requires `--session`)                                                                      |

**Output files** — written under `comis-support-<ts>/`:

| File                  | Contents                                                                                                                                                                                                                                                                                  |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `issue-summary.md`    | Human-readable triage summary, ready to paste into a bug report                                                                                                                                                                                                                           |
| `ai-issue-draft.md`   | A GitHub issue draft with the auto-known facts (versions, triage status, active signals, doctor summary) pre-filled and `<REQUIRED: … — do not invent>` placeholders for repro steps and expected-vs-actual, so an AI assistant can file a faithful issue without inventing missing facts |
| `triage.json`         | Machine-readable triage verdict (status, active signals, doctor summary)                                                                                                                                                                                                                  |
| `doctor.json`         | Full diagnostic findings from the health checks                                                                                                                                                                                                                                           |
| `fleet.json`          | Cross-session fleet health digest — degraded rate, top error kinds, and findings — over the `--since` window                                                                                                                                                                              |
| `config-posture.json` | Which top-level config sections are present, plus any flagged-key labels — section names only, never config values                                                                                                                                                                        |
| `audit-summary.json`  | Window-scoped security-audit digest — total and per-kind event counts over the `--since` window, read from the offline observability store; omitted when that store is absent                                                                                                             |
| `explain.json`        | *With `--session`.* The per-session incident digest — the same content-free `IncidentReport` as [`comis explain`](#comis-explain), embedded verbatim                                                                                                                                      |
| `trace-exports/`      | *With `--deep`.* A directory holding the redacted per-session trace bundle (the [`comis trace export`](/operations/incident-bundle) output), embedded in place instead of written under the workspace                                                                                     |
| `manifest.json`       | Bundle index with the redaction fingerprint and any partial-section warnings                                                                                                                                                                                                              |

```bash theme={}
# Write a bundle for the last 24h (default window)
comis support-bundle

# Machine-readable summary of where the bundle landed and what it found
comis support-bundle --format json
#   { "bundleDir": "…/support-bundles/comis-support-…", "status": "degraded", "activeSignals": ["daemon_down"] }

# Widen the diagnostic window to 72h
comis support-bundle --since 72
```

In table format the command also prints the copy-paste commands to compress the bundle and open an issue from the summary — **you** run them; the command never does:

```bash theme={}
tar czf comis-support-<ts>.tar.gz -C <parent> comis-support-<ts>
gh issue create --body-file <bundleDir>/issue-summary.md
```

When you did not pass `--session` and a best-effort local scan finds a recently-degraded session, the table view also prints a tip naming that session, so you can re-run with `--session <key> --deep` for a focused per-session bundle.

**Exit codes:**

| Code | Meaning                                                                     |
| ---- | --------------------------------------------------------------------------- |
| `0`  | A bundle was written — including a **degraded** or **partial** triage       |
| `1`  | The bundle could not be produced at all (e.g. the data dir is not writable) |
| `2`  | Usage error (e.g. `--deep` without `--session`)                             |

<Warning>
  **Privacy notice**

  The bundle excludes secrets, message bodies, and raw config values by construction, and a redaction backstop masks value-shaped strings and substitutes absolute paths before anything reaches disk. Redaction is heuristic, though — always treat the bundle as sensitive: share it only with authorized engineers over a secure channel, and delete it after triage.

  With `--deep`, the bundle additionally embeds the redacted **raw session trajectory** (session content, PII-adjacent) under `trace-exports/` — strictly more sensitive than the default digest, and the command's printed notice escalates to match. Handle a `--deep` bundle with the same care as a [`comis trace export`](/operations/incident-bundle).
</Warning>

**No daemon required.** The bundle is assembled entirely from the local `~/.comis` files and the offline health checks, so it works when the daemon is stopped or crash-looping — exactly when you most need it. When the daemon happens to be running, the bundle also records its build version; otherwise that field is simply absent.

***

### `comis whoami`

Show the run's **resolved orchestration capabilities** plus the **remaining per-root budget and outward quota** — "what can I do, and how much budget is left". Backed by `CapabilitiesIntrospectContract` (`capabilities.introspect`, `rpc` scope — read-only, no capability required). The read is **self-scoped**: the daemon resolves the caller from the connection, so an operator token sees the default agent's posture and an in-process agent sees its OWN (never another agent's — the request carries no `agentId`).

The `Caps:` line is the resolved [autonomy-profile](/agents/autonomy) capabilities (`orch:*` strings). When the run has an in-flight root (an active agent turn / spawn tree) the `Budget:` line shows the remaining `tokensRemaining` · `wallClockMsRemaining` (· `$usdRemaining` when the model is priced, `$ uncountable` for a zero-price subscription/Codex model — the token and wall-clock limbs are authoritative regardless), and the `Outward:` line the remaining per-hour outward-send allowance. With no live root (an idle agent, pre-spawn) the budget/quota lines are **omitted** rather than shown as zero.

**`comis whoami` is LIVE-only.** Unlike [`comis explain`](#comis-explain) / [`comis fleet`](#comis-fleet) there is **no `--offline` mode**: the remaining budget/quota lives only in the running daemon's in-memory autonomy state, never on disk, so there is nothing to reconstruct offline. When the daemon is unreachable (or rejects the token) the command **fails clearly** with a non-zero exit — it never fabricates an empty/zero snapshot (a false posture). Use [`comis explain`](#comis-explain)'s `spawnTree` for the post-mortem authorization topology of a *finished* run; `whoami` is for an *in-flight* run's remaining budget.

| Flag                | Type              | Default | Description                                          |
| ------------------- | ----------------- | ------- | ---------------------------------------------------- |
| `--caps`            | `boolean`         | `false` | Show capabilities only (omit the budget/quota lines) |
| `--format <format>` | `table` \| `json` | `table` | Output format                                        |

```bash theme={}
# Show caps + remaining budget/quota (live)
comis whoami

# Capabilities only
comis whoami --caps

# Machine-readable output
comis whoami --format json
```

Requires a gateway token (the `rpc` scope — any valid token works; no admin scope needed, unlike `comis explain` / `comis fleet`). There is no offline fallback — the daemon must be running.

***

### `comis status`

Display a unified system status overview showing daemon, gateway, channel, and agent information. Connects to the daemon via RPC for live data and handles daemon offline gracefully.

| Flag                | Type              | Default | Description   |
| ------------------- | ----------------- | ------- | ------------- |
| `--format <format>` | `table` \| `json` | `table` | Output format |

```bash theme={}
# View system status
comis status

# Machine-readable output
comis status --format json
```

### `comis uninstall`

Remove Comis from the host. Re-invokes `install.sh --uninstall` (downloaded from `comis.ai`) so the installer remains the single source of truth for everything that may have been created during install.

| Flag                 | Type      | Default | Description                                                                                                                                                                              |
| -------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--purge`            | `boolean` | `false` | Also delete `~/.comis`, `/etc/comis`, the `COMIS_EGRESS` egress-logging iptables chain, and (if `--with-cloakbrowser` was used to install) `~/.cloakbrowser` + `~/.cloakbrowser-wrapper` |
| `--remove-user`      | `boolean` | `false` | Linux+root only: also delete the `comis` system user (implies `--purge`)                                                                                                                 |
| `--yes`              | `boolean` | `false` | Skip confirmation prompt                                                                                                                                                                 |
| `--dry-run`          | `boolean` | `false` | Print actions without performing them                                                                                                                                                    |
| `--installer <path>` | `string`  | -       | Use a local `install.sh` instead of downloading                                                                                                                                          |

```bash theme={}
# Uninstall Comis (keeps data)
comis uninstall

# Full purge with no prompts
comis uninstall --purge --yes
```

The uninstaller cleans up everything the installer wrote: the main `comis.service` systemd unit, the optional `comis-xvfb.service` companion (if `--with-xvfb` was used), the CLI binary, and — when `--purge` is set — the data dir, the CloakBrowser binary cache (which can be 200–500 MB depending on cached versions), and the `COMIS_EGRESS` egress-logging iptables chain (the uid-scoped `OUTPUT` jump is unhooked before the chain is flushed and deleted). On `--remove-user` the `comis` system user is dropped too. Sudoers rules and the AppArmor profile are left in place; remove those manually if you're decommissioning the host.

***

## Exit Codes

| Code | Meaning                                                               |
| ---- | --------------------------------------------------------------------- |
| `0`  | Success                                                               |
| `1`  | Error (validation failure, operation failed, critical findings found) |

Commands that detect problems (`doctor`, `health`, `security audit`) exit with code 1 when failures or critical findings are present, making them suitable for CI pipeline gating.

## Related

<CardGroup cols={2}>
  <Card title="Config YAML" icon="file-code" href="/reference/config-yaml">
    Configuration file reference
  </Card>

  <Card title="Environment Variables" icon="key" href="/reference/environment-variables">
    Environment variable reference
  </Card>

  <Card title="Operations Overview" icon="server" href="/operations">
    Deployment and management guides
  </Card>

  <Card title="Secrets Management" icon="lock" href="/security/secrets">
    Managing API keys and passwords
  </Card>
</CardGroup>
