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

# CLI reference

> Every public moda command with its flags, one example invocation, and trimmed output.

Complete reference for the `moda` CLI. Install and authentication are covered in the [CLI overview](/cli/overview); this page lists each command by area.

Conventions used below:

* Flags accept `--flag=value` or `--flag value`. Boolean flags are `--flag`.
* Data commands call the [Data API](/data-api/overview) (`https://moda.dev/api/v1/data`, `x-api-key` auth) and require an API key (`MODA_API_KEY`, profile, or `~/.moda/config.json`). Numeric ranges are validated before the request is sent.
* Example outputs are shown as `--json` prints them (the raw payload, trimmed to representative fields). Piped and CI invocations get a `moda.agent.v1` envelope instead, with the same payload under `data` — see [Using the CLI from agents and CI](/cli/agents).
* Requests time out after 30 seconds per attempt and retry up to 3 times on 429/5xx and network errors (exceptions: `ask` uses a 250-second timeout and retries at most once, on network errors only; `feedback` never retries).

## Setup and diagnostics

### moda init

Interactive project setup: browser login, tenant selection, API key provisioning, agent-rules/skill install, prompt manifest creation with a best-effort first sync, and (by default) a server-side harness analysis. Non-interactive with `--yes`.

```bash theme={"dark"}
moda init --yes --tenant-id=1f7c9a2e-4b3d-4c8e-9a1b-2d3e4f5a6b7c
```

| Flag                                                | Description                                                                                            |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `--yes`                                             | Accept all defaults (non-interactive).                                                                 |
| `--tenant-id=ID`                                    | Tenant to provision (required non-interactively with multiple tenants; `MODA_TENANT_ID` also honored). |
| `--remote` / `--local`                              | Run harness analysis server-side (default) or with a local coding agent.                               |
| `--analyst=auto\|claude\|codex\|cursor\|local-scan` | Analyst adapter for harness analysis.                                                                  |
| `--sdk-integrate` / `--no-sdk-integrate`            | Run your coding agent to install and wire the Moda SDK, or skip it.                                    |
| `--ci-cd` / `--no-ci-cd`                            | Create a GitHub Actions Moda validation workflow, or skip it.                                          |
| `--autosync` / `--no-autosync`                      | Create `.github/workflows/moda-autosync.yml` (syncs prompts and skills on push to `main`), or skip it. |
| `--harness-rescan` / `--no-harness-rescan`          | Create `.github/workflows/moda-harness-rescan.yml` (see [CI rescan](/harness/ci-rescan)), or skip it.  |
| `--harness-rescan-paths=GLOBS`                      | Comma-separated path globs that scope the rescan workflow's triggers (implies `--harness-rescan`).     |
| `--tui` / `--no-tui`                                | Force or suppress the full-screen analyst dashboard on a TTY.                                          |
| `--offline`                                         | Skip the final online validation check.                                                                |

### moda provision

Headless API key provisioning for CI and agents. Prints exactly one JSON credentials document to stdout; exits 4 without a stored session and 5 when a multi-tenant account omits `--tenant-id`. Flags: `--tenant-id`, `--label` (default: hostname), `--save`, `--profile`. Full behavior and output are documented in the [CLI overview](/cli/overview#headless-provisioning-moda-provision).

```bash theme={"dark"}
moda provision --tenant-id=1f7c9a2e-4b3d-4c8e-9a1b-2d3e4f5a6b7c --label=ci-runner
```

### moda status

Concise local setup state (config, credentials, synced artifacts).

```bash theme={"dark"}
moda status --json
```

### moda doctor

Diagnoses local setup and data flow: config, auth, prompt manifest, harness artifacts, and (unless `--offline`) an online connectivity check. Flags: `--json`, `--offline`, `--online`.

```bash theme={"dark"}
moda doctor --json
```

### moda manifest

Machine-readable description of the CLI protocol: commands, global flags, env vars, exit codes, and stream events. `moda --json-schemas` prints the JSON Schemas for the agent output contract.

```bash theme={"dark"}
moda manifest --json
```

```json Output (trimmed) theme={"dark"}
{
  "schema_version": "moda.manifest.v1",
  "cli_version": "1.26.0",
  "invocations": ["bunx @moda-ai/cli", "npx -y @moda-ai/cli", "moda"],
  "exit_codes": [
    { "code": 0, "name": "ok", "meaning": "Command completed successfully." },
    { "code": 4, "name": "auth_required", "meaning": "Run login_command, then resume." }
  ]
}
```

### moda profiles / moda config

`moda profiles list|use <name>|create <name>|doctor [name]` manage local profiles (`prod`, `staging`, `local` by default). `moda config show [--profile=NAME]` prints the resolved configuration.

```bash theme={"dark"}
moda profiles use staging
moda config show
```

### moda keys

`moda keys list` and `moda keys create --name=<label>` manage local key descriptors (metadata about which key belongs to which profile). `moda keys rotate` and `moda keys revoke` are not supported yet and exit with an error; revoke keys in the dashboard at Settings → Ingestion keys.

```bash theme={"dark"}
moda keys list
```

## Sessions

### moda auth login

Opens the browser PKCE login flow and stores a 30-day sliding CLI session. `--screen=signup` opens the signup screen instead.

```bash theme={"dark"}
moda auth login
```

```json Output theme={"dark"}
{ "status": "authenticated", "profile": null }
```

### moda auth status

Reports session state without ever opening a browser. Exit code 0 = valid session, 4 = logged out. `--online` adds a 5-second API reachability probe.

```bash theme={"dark"}
moda auth status --online --json
```

```json Output theme={"dark"}
{
  "loggedIn": true,
  "profile": null,
  "expiresAt": "2026-09-15T09:14:02.000Z",
  "user": { "email": "dev@example.com" },
  "online": { "reachable": true, "ok": true, "tenantCount": 1 }
}
```

### moda auth token

Prints the bare session token plus a newline to stdout in every output mode (built for `$(moda auth token)` composition). Exits 4 when logged out; with `--json` it prints `{"error":"not_authenticated"}` instead of prose.

```bash theme={"dark"}
moda auth token
```

### moda auth whoami

Shows the authenticated user, session expiry, and available tenants.

```bash theme={"dark"}
moda auth whoami
```

### moda auth logout

Revokes the session server-side (best effort) and clears local credentials — including the stored API key. If `MODA_API_KEY` is set in the environment it still takes precedence afterwards; unset it to fully sign out.

```bash theme={"dark"}
moda auth logout
```

```json Output theme={"dark"}
{ "status": "logged_out", "profile": null, "api_key_cleared": true }
```

## Production data

### moda overview

Health briefing built from production data: data-flow status, local sync state (harness, prompts, skills), and the top finding. On a TTY it renders prose; `--raw` returns the raw Data API `/overview` payload instead.

```bash theme={"dark"}
moda overview --days-back=30 --raw --json
```

```json Output (trimmed) theme={"dark"}
{
  "period": { "days": 30 },
  "conversations": { "total": 1841, "trend_pct": 6.2 },
  "frustrations": { "total_analyzed": 412, "frustrated": 9, "at_risk": 14, "rate_pct": 2.2 },
  "tool_failures": { "total": 41, "conversations": 33, "tools": 3 },
  "top_clusters": [],
  "recent_activity": []
}
```

| Flag          | Default | Description                                                 |
| ------------- | ------- | ----------------------------------------------------------- |
| `--days-back` | 7       | Look-back window (1–90).                                    |
| `--raw`       | off     | Return the raw `/overview` payload instead of the briefing. |

### moda clusters

Browse the use-case cluster hierarchy from the latest completed cluster run.

```bash theme={"dark"}
moda clusters --parent-id=node_12
```

```json Output (trimmed) theme={"dark"}
{
  "cluster_run": { "id": "run_2031", "num_categories": 8, "num_clusters": 42, "num_segments": 1930, "completed_at": "2026-08-10T00:00:00Z" },
  "breadcrumb": [{ "node_id": "node_12", "label": "Billing & refunds" }],
  "clusters": [
    { "node_id": "node_44", "label": "Refund retries", "summary": "Users retry failed refunds", "keywords": ["refund", "retry"], "segment_count": 118, "has_children": false, "depth": 1 }
  ],
  "meta": { "total_clusters": 42, "total_segments": 1930 }
}
```

| Flag           | Default | Description                                      |
| -------------- | ------- | ------------------------------------------------ |
| `--parent-id`  | —       | Drill into a category (omit for the root level). |
| `--time-range` | `all`   | `all`, `1h`, `24h`, `3d`, `7d`, `30d`, `90d`.    |

Instead of walking the hierarchy, find a cluster by meaning with `--search`, or resolve a node deep link deterministically with `--node-id`:

```bash theme={"dark"}
moda clusters --search="calendar scheduling conflicts" --limit=3
```

```json Output (trimmed) theme={"dark"}
{
  "clusterRunId": "run_2031",
  "query": "calendar scheduling conflicts",
  "search_mode": "hybrid",
  "matches": [
    {
      "node_id": "node_36",
      "parent_id": "node_3",
      "node_type": "cluster",
      "label": "Managing and rescheduling calendar appointments",
      "keywords": ["rescheduling", "calendar"],
      "segment_count": 24,
      "ancestor_path": [{ "nodeId": "node_3", "label": "Scheduling", "depth": 0 }],
      "similarity": 0.81
    }
  ]
}
```

| Flag        | Default  | Description                                                                                              |
| ----------- | -------- | -------------------------------------------------------------------------------------------------------- |
| `--search`  | —        | Cluster search query (2–200 characters). Routes to task-cluster search instead of the hierarchy walk.    |
| `--node-id` | —        | Resolve one node exactly (deep link); bypasses ranked search.                                            |
| `--mode`    | `hybrid` | `fuzzy`, `semantic`, `hybrid` — cluster search mode. `search_mode` in the response is what actually ran. |
| `--limit`   | 30       | 1–50 (cluster search only).                                                                              |

### moda cluster-conversations

List conversations in one cluster node.

```bash theme={"dark"}
moda cluster-conversations node_44 --limit=2
```

```json Output (trimmed) theme={"dark"}
{
  "cluster": { "node_id": "node_44", "label": "Refund retries", "segment_count": 118 },
  "conversations": [
    { "conversation_id": "conv_5e1a9c2b7d3f4680", "summary": "User retries a failed refund three times", "message_count": 21 }
  ],
  "pagination": { "limit": 2, "offset": 0, "total": 118, "has_more": true }
}
```

| Argument / flag | Default  | Description                   |
| --------------- | -------- | ----------------------------- |
| `<node_id>`     | required | Cluster node ID.              |
| `--limit`       | 10       | 1–100.                        |
| `--offset`      | 0        | Pagination offset (≤ 10,000). |

An unknown node ID returns `cluster: null` with an empty list, not an error.

### moda conversations

Search and filter conversations by summary text, cluster, user, environment, world state, and outcome.

```bash theme={"dark"}
moda conversations --world-state="refund,enterprise" --outcome=negative --limit=2
```

```json Output (trimmed) theme={"dark"}
{
  "conversations": [
    {
      "conversation_id": "conv_5e1a9c2b7d3f4680",
      "summary": "User retries a failed refund three times",
      "message_count": 21,
      "first_timestamp": "2026-08-14T18:02:10Z",
      "last_timestamp": "2026-08-14T18:41:55Z",
      "cluster_id": "node_44",
      "cluster_name": "Refund retries",
      "environment": "production"
    }
  ],
  "search_mode": "keyword",
  "pagination": { "limit": 2, "offset": 0, "total": 7, "has_more": true }
}
```

| Flag                    | Default | Description                                                                                                                                          |
| ----------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--search`              | —       | Text match on conversation summaries and message bodies.                                                                                             |
| `--cluster-id`          | —       | Filter by cluster node ID.                                                                                                                           |
| `--user-id`             | —       | Filter to one user.                                                                                                                                  |
| `--time-range`          | `all`   | `all`, `1h`, `24h`, `3d`, `7d`, `30d`, `90d`.                                                                                                        |
| `--environment`         | `all`   | `all`, `development`, `staging`, `production`.                                                                                                       |
| `--world-state`         | —       | Keyword match (case-insensitive substring) over world-state slots and the durable user profile; comma-separated terms are ANDed. Max 200 characters. |
| `--outcome`             | `any`   | `positive` (high blended segment score, not frustrated) or `negative` (low blended score or frustrated).                                             |
| `--include-world-state` | off     | Attach each result's world-state summary (one extra request per row).                                                                                |
| `--limit` / `--offset`  | 20 / 0  | Pagination (limit 1–100, offset ≤ 10,000).                                                                                                           |

### moda search

Message-grain search across conversations: keyword, semantic, or hybrid.

```bash theme={"dark"}
moda search "refund failed" --mode=hybrid --time-range=7d --limit=1
```

```json Output theme={"dark"}
{
  "query": "refund failed",
  "search_mode": "hybrid",
  "degrade_reason": "none",
  "results": [
    {
      "conversation_id": "conv_5e1a9c2b7d3f4680",
      "message_index": 12,
      "role": "user",
      "timestamp": "2026-08-14T18:22:41Z",
      "snippet": "The refund failed again after I retried the payment.",
      "score": 0.0328,
      "cluster_id": "node_44",
      "cluster_name": "Refund retries",
      "conversation_summary": "User retries a failed refund three times"
    }
  ],
  "totals": { "matched_conversations": 18, "prior_window_matched_conversations": 25 },
  "pagination": { "limit": 1, "returned": 1, "has_more": true }
}
```

| Argument / flag     | Default  | Description                                   |
| ------------------- | -------- | --------------------------------------------- |
| `"<query>"`         | required | 1–500 characters.                             |
| `--mode`            | `hybrid` | `keyword`, `semantic`, or `hybrid`.           |
| `--user-id`         | —        | Filter to one user.                           |
| `--time-range`      | `all`    | `all`, `1h`, `24h`, `3d`, `7d`, `30d`, `90d`. |
| `--limit`           | 20       | 1–100. No offset — pagination is limit-only.  |
| `--include-tool-io` | off      | Include tool input/output blocks in results.  |

`search_mode` in the response is the mode that actually ran: semantic and hybrid degrade to keyword when embeddings are unavailable (`degrade_reason: "semantic_unavailable"`, retry may fix) or no hit clears the relevance floor (`"below_relevance_floor"`, retry will not fix). Scores are not comparable across modes. Each result carries `conversation_id` + `message_index` — feed them to `moda context`.

### moda context

Windowed context around one message in a conversation.

```bash theme={"dark"}
moda context conv_5e1a9c2b7d3f4680 --msg-index=12 --window=1
```

```json Output (trimmed) theme={"dark"}
{
  "conversation_id": "conv_5e1a9c2b7d3f4680",
  "total_messages": 21,
  "summary": "User retries a failed refund three times",
  "context": {
    "center_index": 12,
    "from_index": 11,
    "to_index": 13,
    "messages": [
      { "index": 12, "role": "user", "content": "The refund failed again after I retried the payment.", "tool_calls": [], "tool_results": [], "timestamp": "2026-08-14T18:22:41Z" }
    ]
  }
}
```

| Argument / flag     | Default                    | Description                      |
| ------------------- | -------------------------- | -------------------------------- |
| `<conversation_id>` | required                   | Conversation to read.            |
| `--msg-index`       | middle of the conversation | Center message index.            |
| `--window`          | 2                          | Turns of context per side (1–5). |

### moda world-state

A conversation's [world state](/concepts/data-model): durable user profile, per-segment slots, open threads, and the event stream.

```bash theme={"dark"}
moda world-state conv_5e1a9c2b7d3f4680 --summary-only
```

```json Output (trimmed) theme={"dark"}
{
  "hasData": true,
  "userId": "user_1842",
  "userDurableProfile": { "slots": { "plan": "enterprise" }, "updatedAt": "2026-08-14T18:30:02Z", "slotCount": 6 },
  "segments": [
    {
      "segmentIndex": 0,
      "startMsgIdx": 0,
      "endMsgIdx": 14,
      "slots": { "refund_status": "failed" },
      "openThreads": ["refund retry"],
      "slotCounts": { "durable": 2, "segmentLocal": 3, "total": 5 }
    }
  ],
  "summary": { "eventCount": 42, "segmentCount": 2, "lastUpdatedAt": "2026-08-14T18:30:02Z" }
}
```

| Argument / flag     | Default  | Description                        |
| ------------------- | -------- | ---------------------------------- |
| `<conversation_id>` | required | Conversation to read.              |
| `--summary-only`    | off      | Omit the per-message event stream. |
| `--event-limit`     | 2000     | Max events to return (1–5,000).    |

**Point-in-time snapshot** — the folded slot state at an exact `(msg_index, block_index)` tick: what the agent believed at that turn.

```bash theme={"dark"}
moda world-state conv_5e1a9c2b7d3f4680 --snapshot --msg-index=42
```

```json Output (trimmed) theme={"dark"}
{
  "found": true,
  "segmentIndex": 1,
  "msgIndex": 41,
  "blockIndex": 2,
  "slots": { "refund_status": { "value": "failed", "msg_index": 38, "receipt_id": "rcpt_91" } }
}
```

`msgIndex`/`blockIndex` in the response are the tick of the last applied state change at or before the requested tick. An unknown conversation (or no state at that tick) returns `found: false` with empty slots and the CLI adds a not-found warning.

| Flag              | Default | Description                                                                  |
| ----------------- | ------- | ---------------------------------------------------------------------------- |
| `--snapshot`      | off     | Fold state up to the requested tick.                                         |
| `--msg-index`     | 0       | 0–100,000.                                                                   |
| `--block-index`   | 0       | 0–100,000. Events at `msg-index` with a block at or below this are included. |
| `--segment-index` | —       | Scope the fold to one segment.                                               |

**Replay** — state evolution over time, frame by frame or as raw fold inputs.

```bash theme={"dark"}
moda world-state conv_5e1a9c2b7d3f4680 --replay --message-count=50
moda world-state conv_5e1a9c2b7d3f4680 --replay --format=fold-inputs
```

Frames mode returns exactly `--message-count` frames (`msgIndex`, `slots`, `openThreads`, `hasSnapshot`), carrying state forward between frames; **`--message-count` is required in frames mode** — the API returns no frames without it, so the CLI rejects the invocation up front. `fold-inputs` mode returns the raw keyframes and events (snake\_case fields, no count needed). `hasData: false` means the conversation has no state; the CLI adds a not-found warning.

| Flag              | Default  | Description                                                    |
| ----------------- | -------- | -------------------------------------------------------------- |
| `--replay`        | off      | Return state over time instead of the current state.           |
| `--message-count` | —        | Frames to fold (1–1,000,000). Required with `--format=frames`. |
| `--format`        | `frames` | `frames` or `fold-inputs`.                                     |

### moda audit

Raw span read for a conversation or trace: hierarchy, orphans, duplicates. Useful for debugging ingestion. Alias: `moda trace`.

```bash theme={"dark"}
moda audit conv_5e1a9c2b7d3f4680
```

```json Output (trimmed) theme={"dark"}
{
  "summary": {
    "total_spans": 18,
    "by_type": { "llm": 9, "tool": 8, "agent": 1 },
    "trace_ids": ["4bf92f3577b34da6a3ce929d0e0e4736"],
    "orphan_count": 0,
    "duplicate_count": 0
  },
  "spans": [
    { "span_id": "00f067aa0ba902b7", "parent_span_id": null, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "type": "llm" }
  ],
  "orphans": [],
  "duplicates": []
}
```

| Argument / flag               | Default  | Description                                                     |
| ----------------------------- | -------- | --------------------------------------------------------------- |
| `<conversation_id\|trace_id>` | required | ID to audit.                                                    |
| `--kind`                      | `auto`   | `auto` (try trace, then conversation), `trace`, `conversation`. |
| `--include-raw`               | off      | Attach verbatim raw event bodies.                               |
| `--limit`                     | 200      | Max spans (1–1,000).                                            |

### moda step-scores

Graph-PRM step scores for one conversation: per-segment progress curves, the first bad step, and the blended rollup. This is the reward evidence that `moda problems` and `moda ask` cite.

```bash theme={"dark"}
moda step-scores conv_5e1a9c2b7d3f4680
```

```json Output (trimmed) theme={"dark"}
{
  "conversation_id": "conv_5e1a9c2b7d3f4680",
  "segments": [
    {
      "segment_id": "seg_3f2a",
      "segment_status_at_scoring": "closed",
      "failure_type": { "failure_type": "tool_misuse", "score": 0.71 },
      "steps": [
        { "unit_id": "unit_18", "message_index": 12, "progress_score": 0.44, "first_bad_step_probability": 0.62, "first_bad_step_reason": "Retried the refund tool without changing arguments." }
      ]
    }
  ],
  "rollup": { "blended_score": 0.58, "segment_count": 2, "weighted_segment_count": 1.6, "total_weight": 2.0 }
}
```

| Argument            | Default  | Description           |
| ------------------- | -------- | --------------------- |
| `<conversation_id>` | required | Conversation to read. |

An unknown or not-yet-scored conversation returns the empty shape (`segments: []`, `rollup: null`) with a CLI warning — the API does not 404. Very large conversations can exceed the output budget; the payload is then replaced with a truncation stub (`truncated`, `original_bytes`).

### moda frustrations

User frustration detections with evidence quotes. The CLI adds an `anchor` block per row (derived from `key_turns`/`user_quotes`) so agents can jump straight to `moda context`.

Frustration is the legacy single-family view. The current multi-family emotion model — frustration, sadness, confusion, anxiety, trust, positive — is served by [`moda emotions`](#moda-emotions), which analyzes a much larger sample. Prefer `emotions` for new work.

```bash theme={"dark"}
moda frustrations --days-back=14 --limit=1
```

```json Output (trimmed) theme={"dark"}
{
  "summary": { "total_analyzed": 412, "frustrated_count": 9, "at_risk_count": 14, "frustration_rate_pct": 2.2 },
  "frustrations": [
    {
      "conversation_id": "conv_5e1a9c2b7d3f4680",
      "is_frustrated": true,
      "frustration_score": 0.91,
      "risk_score": 0.88,
      "primary_cause": "Refund tool errored twice; the agent asked the user to retry.",
      "user_quotes": [{ "turn": 12, "quote": "This is the third time I am asking.", "signal": "exasperation" }],
      "key_turns": [12, 16],
      "message_count": 21,
      "detected_at": "2026-08-15T07:03:11Z",
      "anchor": {
        "kind": "frustration",
        "conversation_id": "conv_5e1a9c2b7d3f4680",
        "msg_index": 12,
        "signal": "exasperation",
        "quote_preview": "This is the third time I am asking.",
        "all_turns": [12, 16],
        "no_anchor": false
      }
    }
  ],
  "pagination": { "limit": 1, "offset": 0, "total": 9, "has_more": true }
}
```

| Flag               | Default | Description                                      |
| ------------------ | ------- | ------------------------------------------------ |
| `--days-back`      | 7       | 1–90.                                            |
| `--limit`          | 10      | 1–20.                                            |
| `--offset`         | 0       | Pagination offset.                               |
| `--include-window` | off     | Attach a conversation window around each anchor. |
| `--window`         | 1       | Turns per side for `--include-window` (1–5).     |

### moda emotions

Multi-family emotion detections: `frustration`, `sadness`, `confusion`, `anxiety`, `trust`, and `positive`. The summary always covers all families; `--family` filters the paginated detections list only.

```bash theme={"dark"}
moda emotions --family=confusion --days-back=7 --limit=1
```

```json Output (trimmed) theme={"dark"}
{
  "summary": {
    "conversations_analyzed": 11482,
    "by_family": { "frustration": 231, "sadness": 164, "confusion": 118, "anxiety": 51, "trust": 47, "positive": 1893 },
    "negative_rate_pct": 4.6,
    "positive_rate_pct": 17.3,
    "repair_rate_pct": 23.2
  },
  "signal_breakdown": [{ "family": "confusion", "signal": "repeated_clarification", "count": 61 }],
  "detections": [
    {
      "detection_id": "7404fcab-7141-4a04-9de9-e4b26659a755",
      "conversation_id": "conv_5e1a9c2b7d3f4680",
      "family": "confusion",
      "is_detected": true,
      "score": 0.84,
      "primary_cause": "Agent restated the plan three times without answering the pricing question.",
      "user_quotes": [{ "turn": 9, "quote": "I still don't understand what I'm being charged for.", "signal": "confusion" }],
      "key_turns": [9],
      "detected_at": "2026-08-15T11:02:44"
    }
  ],
  "pagination": { "limit": 1, "offset": 0, "total": 118, "has_more": true }
}
```

| Flag          | Default | Description                                                                                                                             |
| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `--family`    | —       | `frustration`, `sadness`, `confusion`, `anxiety`, `trust`, `positive`. Filters detections only.                                         |
| `--days-back` | 7       | 1–90. Windows on analysis time, not `detected_at` (the anchor turn's timestamp, which can be much older on long-running conversations). |
| `--limit`     | 10      | 1–20.                                                                                                                                   |
| `--offset`    | 0       | 0–10,000.                                                                                                                               |

### moda hallucinations

Grounding detections: assistant claims that contradict or are verified against the conversation's world state, with the rule that fired and the offending substring.

```bash theme={"dark"}
moda hallucinations --kind=contradicted --limit=1
```

```json Output (trimmed) theme={"dark"}
{
  "summary": {
    "total_scored": 124,
    "contradicted_count": 29,
    "verified_count": 23,
    "ungrounded_count": 41,
    "ungrounded_rate": 0.331,
    "conversations_scored": 99,
    "rule_breakdown": [{ "source": "rule", "rule_id": "false_absence", "count": 17 }]
  },
  "detections": [
    {
      "conversation_id": "conv_5e1a9c2b7d3f4680",
      "message_index": 31,
      "kind": "contradicted",
      "label": "CONTRADICT",
      "confidence": 0.92,
      "rule_id": "false_absence",
      "violated_slot_key": "contact.email",
      "offending_substring": "I couldn't find a verified personal email for him.",
      "detected_at": "2026-08-15T09:41:07"
    }
  ],
  "pagination": { "limit": 1, "offset": 0, "total": 29 }
}
```

| Flag                | Default | Description                                                                                  |
| ------------------- | ------- | -------------------------------------------------------------------------------------------- |
| `--kind`            | —       | `contradicted` or `verified`. Filters the detections list.                                   |
| `--conversation-id` | —       | Scope to one conversation. Note: this narrows the summary aggregates too, not just the list. |
| `--days-back`       | 7       | 1–90.                                                                                        |
| `--limit`           | 10      | 1–20.                                                                                        |
| `--offset`          | 0       | 0–10,000.                                                                                    |

The detections list contains only contradicted and verified rows; plain ungrounded rows appear in the summary counts but are not listed. `pagination` here has no `has_more` field — compute `offset + limit < total` client-side.

### moda tool-failures

Per-tool failure overview for the window.

```bash theme={"dark"}
moda tool-failures --days-back=7
```

```json Output (trimmed) theme={"dark"}
{
  "summary": { "total": 41, "total_calls": 1876, "conversations": 33, "tools": 3, "failure_rate_pct": 2.2 },
  "tools": [
    {
      "tool_name": "lookupCustomer",
      "failure_count": 28,
      "total_count": 512,
      "failure_rate_pct": 5.5,
      "conversation_count": 22,
      "top_error": "TimeoutError: request to CRM timed out after 10000ms",
      "last_seen": "2026-08-15T22:41:09Z"
    }
  ]
}
```

| Flag          | Default | Description |
| ------------- | ------- | ----------- |
| `--days-back` | 7       | 1–90.       |

### moda tool-failure-detail

Failure subtypes and concrete examples for one tool. Each example carries a CLI-added `anchor` (conversation + message index).

```bash theme={"dark"}
moda tool-failure-detail lookupCustomer --limit=1
```

```json Output (trimmed) theme={"dark"}
{
  "tool_name": "lookupCustomer",
  "subtypes": [
    { "subtype": "timeout.expired", "count": 21, "conversation_count": 17, "sample_error": "TimeoutError: request to CRM timed out after 10000ms", "last_seen": "2026-08-15T22:41:09Z" }
  ],
  "examples": [
    {
      "conversation_id": "conv_5e1a9c2b7d3f4680",
      "error_message": "TimeoutError: request to CRM timed out after 10000ms",
      "error_subtype": "timeout.expired",
      "msg_index": 9,
      "detected_at": "2026-08-15T22:41:09Z",
      "anchor": { "kind": "tool_failure", "conversation_id": "conv_5e1a9c2b7d3f4680", "msg_index": 9, "tool_name": "lookupCustomer", "error_subtype": "timeout.expired", "no_anchor": false }
    }
  ],
  "pagination": { "limit": 1, "offset": 0, "total": 28, "has_more": true }
}
```

| Argument / flag                 | Default  | Description                                     |
| ------------------------------- | -------- | ----------------------------------------------- |
| `<tool_name>`                   | required | Tool to inspect.                                |
| `--subtype`                     | —        | Filter examples to one failure subtype.         |
| `--days-back`                   | 7        | 1–90.                                           |
| `--limit`                       | 5        | 1–20.                                           |
| `--offset`                      | 0        | Pagination offset.                              |
| `--include-window` / `--window` | off / 1  | Attach conversation windows around each anchor. |

### moda problems

The ranked cross-signal problem list (what to fix first). The CLI appends `dashboard_url` for the corresponding dashboard page.

```bash theme={"dark"}
moda problems --days-back=30 --limit=1
```

```json Output (trimmed) theme={"dark"}
{
  "summary": { "days_back": 30, "open_problems": 4, "reopened": 1, "total_problems": 11, "signals_attributed": 132, "explained_coverage_pct": 78.6 },
  "problems": [
    {
      "problem_id": "3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b",
      "display_name": "CRM lookups time out during peak hours",
      "cause_statement": "lookupCustomer calls exceed the 10s timeout under load, so refund flows stall.",
      "lifecycle": "open",
      "rank_score": 42.5,
      "affected_users": 19,
      "affected_conversations": 44
    }
  ],
  "untrusted": [],
  "dashboard_url": "https://moda.dev/dashboard/problems"
}
```

| Flag          | Default | Description |
| ------------- | ------- | ----------- |
| `--days-back` | 30      | 1–90.       |
| `--limit`     | 25      | 1–25.       |

### moda problem

Open one Problem: the full dossier, or a paged sub-resource view. The dossier includes the rubric, rank trend, sub-problems, verification results, evidence, affected conversations, investigation reports, confidence, hierarchy, and representative stories.

```bash theme={"dark"}
moda problem 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b
moda problem 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b --evidence --limit=10
moda problem 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b --reports
moda problem 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b --conversations --family=tool_failure
moda problem 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b --feedback
```

```json Output (dossier, trimmed) theme={"dark"}
{
  "found": true,
  "problem_id": "3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b",
  "alias_resolved": false,
  "header": { "display_name": "CRM lookups time out during peak hours", "lifecycle": "open", "rank_score": 42.5 },
  "trend": [{ "run_ts": "2026-08-15T00:00:00", "rank_score": 42.5, "total_attributions": 132 }],
  "verification": { "audit_pass_rate": 0.86, "judge_audit": { "passed": 12, "total": 14 } },
  "evidence": [{ "attribution_id": "a4b73e58-48de-4fb2-9c58-1c3d5e7f9a0b", "conversation_id": "conv_5e1a9c2b7d3f4680", "causal_rationale": "Timeout fired before the CRM responded.", "door": "rubric_match" }],
  "investigation_reports": { "items": [], "has_more": false, "next_cursor": null },
  "stories": []
}
```

| Argument / flag   | Default                                   | Description                                                                                                            |
| ----------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `<problem_id>`    | required                                  | Problem ID (UUIDs from `moda problems`; merged-away IDs resolve to the surviving problem with `alias_resolved: true`). |
| `--evidence`      | off                                       | Paged attribution evidence (rationales, anchors, replay verification).                                                 |
| `--reports`       | off                                       | Paged investigation reports (mechanism, narrative, citations).                                                         |
| `--conversations` | off                                       | Paged affected conversations with deep-link anchors.                                                                   |
| `--feedback`      | off                                       | The problem's feedback queue and pending count.                                                                        |
| `--limit`         | 25 (evidence/conversations), 20 (reports) | 1–50.                                                                                                                  |
| `--cursor`        | —                                         | Keyset cursor from the previous page's `next_cursor`. Opaque — pass it back verbatim.                                  |
| `--family`        | —                                         | `--conversations` filter: `tool_failure`, `emotion`, `laziness`, `hallucination`, `prm_dip`.                           |
| `--door`          | —                                         | `--conversations` filter: `rubric_match`, `local_causal_chain`.                                                        |

At most one view flag per invocation. The dossier endpoint never returns 404 — an unknown or retired ID comes back `found: false` and the CLI adds a not-found warning. Sub-resource views require a canonical UUID (the CLI rejects other ids before the network). Cursors are not portable across different `--family`/`--door` filters.

### moda problem-feedback

Close the loop on a Problem from the terminal: mark it fixed, dismiss it, rename it, or flag a bad attribution. Writes to the same reconcile queue as the dashboard.

```bash theme={"dark"}
moda problem-feedback 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b --action=mark_fixed
moda problem-feedback 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b --action=dismiss --reason="not actionable"
moda problem-feedback 3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b --action=rename --new-name="CRM lookup timeouts"
```

```json Output theme={"dark"}
{ "success": true, "feedback_id": "5b0c2f1e-8d43-4a6a-9c1d-7e2f3a4b5c6d", "problem_id": "3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b", "action": "mark_fixed", "status": "pending", "duplicate": false }
```

| Argument / flag    | Default    | Description                                                                         |
| ------------------ | ---------- | ----------------------------------------------------------------------------------- |
| `<problem_id>`     | required   | Canonical problem UUID.                                                             |
| `--action`         | required   | `mark_fixed`, `dismiss`, `flag_attribution`, `rename`.                              |
| `--reason`         | —          | Required for `dismiss` and `flag_attribution` (≤ 2,000 characters).                 |
| `--attribution-id` | —          | Required UUID for `flag_attribution`; find IDs with `moda problem <id> --evidence`. |
| `--new-name`       | —          | Required for `rename` (≤ 255 characters).                                           |
| `--actor`          | `moda-cli` | Recorded actor label.                                                               |

The write is idempotent while the previous identical submission is still pending (`duplicate: true`, no new row). The CLI enforces the conditional requirements locally, so a bad invocation fails before the network.

### moda feedback

Flag wrong or missing data (or CLI quirks) to the Moda team. The note is required; IDs attach context.

```bash theme={"dark"}
moda feedback "cluster label looks wrong" --category=bad_cluster_label --cluster-id=node_44
```

```json Output theme={"dark"}
{ "submitted": true, "category": "bad_cluster_label", "severity": "low", "feedback_id": "5b0c2f1e-8d43-4a6a-9c1d-7e2f3a4b5c6d", "submitted_at": "2026-08-16T09:14:02Z" }
```

| Argument / flag                                                | Default  | Description                                                                                                                                |
| -------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `"<note>"`                                                     | required | Up to 4,000 characters.                                                                                                                    |
| `--category`                                                   | `other`  | `bad_cluster_label`, `mismatched_frustration`, `missing_data`, `noisy_data`, `wrong_tool_failure`, `incorrect_loop`, `api_quirk`, `other`. |
| `--severity`                                                   | `low`    | `info`, `low`, `medium`, `high`.                                                                                                           |
| `--conversation-id`, `--cluster-id`, `--tool-name`, `--run-id` | —        | Attach references.                                                                                                                         |

### moda tail

Live-tail production activity: poll the Data API and emit one JSON line per newly seen item (NDJSON on stdout in every output mode — a tail is a stream, so the single-envelope agent contract does not apply). `Ctrl-C` stops it.

```bash theme={"dark"}
moda tail                                # new conversations, every 15s
moda tail --signal=all --interval=30     # conversations + emotion detections
moda tail --once --max-events=10         # one poll (baseline page), bounded
```

```json Output (one line per item) theme={"dark"}
{"type":"conversation","conversation_id":"conv_5e1a9c2b7d3f4680","timestamp":"2026-08-16T08:19:11","message_count":21,"environment":"production","cluster_name":"Refund retries","summary":"User retries a failed refund"}
{"type":"emotion","detection_id":"7404fcab-7141-4a04-9de9-e4b26659a755","conversation_id":"conv_5e1a9c2b7d3f4680","family":"frustration","primary_cause":"Refund tool errored twice.","detected_at":"2026-08-15T11:02:44"}
{"type":"tail_coverage","signal":"emotions","complete":false,"scanned":500,"total":3823,"coverage_pct":13.1,"pages_scanned":25,"detail":"..."}
```

| Flag           | Default         | Description                                                                                                                                    |
| -------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `--signal`     | `conversations` | `conversations`, `emotions`, `all`.                                                                                                            |
| `--interval`   | 15              | Seconds between polls (5–3,600).                                                                                                               |
| `--once`       | off             | One poll, then exit.                                                                                                                           |
| `--limit`      | 20              | Conversations page size per poll (1–50).                                                                                                       |
| `--max-events` | —               | Stop after N emitted stdout records, metadata included (enforced per record, so a large page never overshoots).                                |
| `--full-scan`  | off             | Emotions: scan the whole one-day window instead of the top-scoring prefix (many more requests, bounded by the API's offset ceiling of 10,000). |

Dedupe is in-memory per process: conversations re-emit when they advance (`message_count` changes — long-running, single-conversation-per-user tenants update in place); emotion detections emit once per `detection_id`.

**Emotions coverage.** The emotions endpoint is ranked by score with no time ordering or cursor, so a tail cannot ask for "detections since X" — by default it follows the highest-scoring detections and emits a `tail_coverage` record per poll stating exactly what it scanned (`scanned`, `total`, `coverage_pct`, `complete`). On high-volume tenants the default poll covers a fraction of the day; use `--full-scan` when completeness matters more than request count. `--max-events` bounds every record on stdout, metadata included. When it is set, one slot is reserved for the coverage record so a capped tail still reports what it saw; at `--max-events=1` the data row keeps the slot and the coverage record is written to stderr instead, where it cannot affect a consumer's record count.

## Production intelligence

### moda investigate

Aggregates overview, tool failures, and frustrations into ranked findings with evidence references and next commands. `--tool=NAME` scopes to one tool; `--conversation=ID` records the conversation you are investigating.

```bash theme={"dark"}
moda investigate --days-back=7 --tool=lookupCustomer
```

```json Output (trimmed) theme={"dark"}
{
  "schemaVersion": "production_investigation.v0.1",
  "daysBack": 7,
  "summary": { "text": "Top production finding: lookupCustomer is failing in production. lookupCustomer produced 28 failed call(s) in the last 7 day(s).", "confidence": "high" },
  "findings": [
    { "id": "finding_tool_failure_lookupCustomer", "kind": "tool_failure", "title": "lookupCustomer is failing in production", "severity": "critical", "confidence": "high", "impactScore": 115 }
  ],
  "nextCommands": ["moda tool-failure-detail lookupCustomer --include-window"],
  "sourceStatus": { "overview": "ok", "tools": "ok", "frustrations": "ok" }
}
```

### moda failures

Same investigation, restricted to failure findings (frustration findings excluded). Flags: `--days-back`, `--tool`.

```bash theme={"dark"}
moda failures --tool=lookupCustomer
```

### moda ask

Ask Moda a production question in natural language. On a TTY the answer streams live; add `--no-stream` for a buffered request. The cloud request times out after `MODA_ASK_TIMEOUT_MS` (default 250 s); if Moda Cloud is unavailable the CLI synthesizes an answer from local Data API evidence and **exits 3** with `source: "local_fallback"` and `degraded: true`.

```bash theme={"dark"}
moda ask "what should I fix first?" --json
```

```json Output (trimmed) theme={"dark"}
{
  "schema_version": "moda.intelligence.v1",
  "source": "cloud",
  "degraded": false,
  "summary": { "text": "Fix lookupCustomer timeouts first.", "confidence": "high" },
  "answer_markdown": "The strongest production signal this week is lookupCustomer timing out in 5.5% of calls, affecting 22 conversations...",
  "answer_claims": []
}
```

| Argument / flag | Default  | Description                      |
| --------------- | -------- | -------------------------------- |
| `"<question>"`  | required | The question.                    |
| `--days-back`   | 7        | Evidence window (1–90).          |
| `--no-stream`   | off      | Disable live streaming on a TTY. |

## Prompt management

Code-first prompt workflow — see [Prompt management](/prompt-management/overview) for concepts and [Workflow](/prompt-management/workflow) for the end-to-end guide. Running `moda prompts` with no subcommand is equivalent to `moda prompts status`.

### moda prompts init

Creates `.moda/prompts.yml` if absent (never overwrites). The default manifest discovers `prompts/**/*.prompt.{md,json,yaml,yml}`.

```bash theme={"dark"}
moda prompts init
```

```json Output theme={"dark"}
{
  "manifest": ".moda/prompts.yml",
  "lockfile": ".moda/prompts.lock.json",
  "promptPaths": ["prompts/**/*.prompt.md", "prompts/**/*.prompt.json", "prompts/**/*.prompt.yaml", "prompts/**/*.prompt.yml"]
}
```

### moda prompts status / moda prompts diff

Local-only comparison of discovered prompt files against `.moda/prompts.lock.json`. No network call. `diff` is an alias for `status` (it is not a textual diff). Per-prompt `state` is `new`, `changed`, `unchanged`, or `deleted`.

```bash theme={"dark"}
moda prompts status
```

```json Output (trimmed) theme={"dark"}
{
  "manifest": ".moda/prompts.yml",
  "lockfile": ".moda/prompts.lock.json",
  "total": 3,
  "changed": 1,
  "new": 0,
  "deleted": 0,
  "prompts": [
    { "key": "support.triage", "sourcePath": "prompts/support/triage.prompt.md", "contentHash": "b41f0c…", "lockedHash": "a92c7e…", "versionId": "pver_1f2e3d4c5b6a79880911223344556677", "state": "changed" }
  ]
}
```

### moda prompts sync

Uploads all discovered prompts (unchanged ones are server-side no-ops — versions are content-addressed and immutable), moves each prompt's `current` pointer to the synced version, and writes `.moda/prompts.lock.json`.

```bash theme={"dark"}
moda prompts sync
```

```json Output (trimmed) theme={"dark"}
{
  "synced": [
    { "key": "support.triage", "promptId": "prompt_0a1b2c3d4e5f60718293a4b5", "versionId": "pver_1f2e3d4c5b6a79880911223344556677", "contentHash": "b41f0c…", "sourcePath": "prompts/support/triage.prompt.md" }
  ],
  "lockfile": ".moda/prompts.lock.json"
}
```

| Flag         | Default | Description                                                |
| ------------ | ------- | ---------------------------------------------------------- |
| `--dry-run`  | off     | Compute IDs without writing anything (no lockfile update). |
| `--watch`    | off     | Poll local files and re-sync on change.                    |
| `--interval` | 2500    | Poll interval in ms for `--watch` (min 1,000).             |

### moda prompts promote

Moves a release label (`dev`, `staging`, `prod`) to a specific version. Returns the updated prompt with all versions.

```bash theme={"dark"}
moda prompts promote support.triage --label=prod --version=pver_1f2e3d4c5b6a79880911223344556677
```

| Argument / flag | Description                                                                 |
| --------------- | --------------------------------------------------------------------------- |
| `<key>`         | Prompt key (required).                                                      |
| `--label`       | `dev`, `staging`, or `prod` (required).                                     |
| `--version`     | Target version ID from the lockfile (`--version-id` is an alias; required). |

<Note>
  A `dev` promotion moves the same pointer that every sync overwrites, so it is replaced by the next `moda prompts sync`. Use `staging`/`prod` for stable release labels.
</Note>

### moda prompts ab

Judged A/B replay comparison between a baseline and a candidate prompt. Builds (or reuses) a replay set, runs both arms, and reports a verdict — the candidate wins only with strictly more passed cases. See [Experiments](/prompt-management/experiments).

```bash theme={"dark"}
moda prompts ab --baseline=prompts/support/triage.prompt.md --candidate=prompts/support/triage-v2.prompt.md --conversations=conv_5e1a9c2b7d3f4680,conv_8c33d2f1a09b44e7
```

The command polls the run every 15 seconds and prints per-case results plus the final verdict; `--no-wait` returns immediately after enqueueing.

| Flag                            | Default     | Description                                             |
| ------------------------------- | ----------- | ------------------------------------------------------- |
| `--baseline` / `--candidate`    | required    | Prompt file paths or synced prompt keys.                |
| `--set-id`                      | —           | Reuse an existing replay set (`--replay-set-id` alias). |
| `--conversations`               | —           | Comma-separated conversation IDs to build a set from.   |
| `--cases`                       | 5           | Cases for an auto-generated set.                        |
| `--lookback-days`               | 30          | Window for auto-generated cases.                        |
| `--seeds`                       | 3           | Seeds per case (max 10).                                |
| `--model`                       | —           | Assistant model override for the replay.                |
| `--sync`                        | off         | Run `moda prompts sync` first.                          |
| `--no-wait`                     | off         | Return after enqueue instead of polling.                |
| `--timeout` / `--poll-interval` | 2 h / 15 s  | Polling bounds.                                         |
| `--tenant-id`                   | from config | Tenant to run in.                                       |

### moda prompts propose

Generates a revised, unlabeled candidate version from a failed A/B run's failure evidence.

```bash theme={"dark"}
moda prompts propose support.triage --from-run=run_9d2e --set-id=rset_31ab --out=prompts/support/triage-v2.prompt.md
```

| Argument / flag    | Default  | Description                                                               |
| ------------------ | -------- | ------------------------------------------------------------------------- |
| `<key>`            | required | Prompt key (`--prompt-key` alias).                                        |
| `--from-run`       | required | Source A/B run ID (`--from-run-id` / `--run-id` aliases).                 |
| `--set-id`         | required | Replay set ID (`--replay-set-id` alias).                                  |
| `--max-dossiers`   | 8        | Failure dossiers fed to the revision (max 16).                            |
| `--out`            | —        | Write the candidate content to a file.                                    |
| `--gate`           | off      | Auto-run an A/B of candidate vs. baseline over the replay set.            |
| `--promote-on-win` | off      | Promote the candidate to `prod` only on a strict win (requires `--gate`). |

## Skills

Moda can distill recurring agent behavior into skill files and sync them with your repo. Skill commands talk to the Ingestion API host (`MODA_INGEST_URL`) and the control plane using your API key.

### moda skills gen

Kicks off tenant-wide skill generation and returns a run ID.

```bash theme={"dark"}
moda skills gen
```

```text Output theme={"dark"}
accepted: true
generation_run_id: 51f2b3a4-9c8d-4e7f-a1b2-c3d4e5f6a7b8
follow progress: moda skills status 51f2b3a4-9c8d-4e7f-a1b2-c3d4e5f6a7b8
```

Flags: `--source=all|sdk`, `--max-sessions=N`, `--start-at=ISO`, `--end-at=ISO`, `--wait`.

### moda skills status

Status, outcome, and recent events for a generation run (latest run when no ID is given). Exits 1 when the run failed.

```bash theme={"dark"}
moda skills status 51f2b3a4-9c8d-4e7f-a1b2-c3d4e5f6a7b8
```

### moda skills pull

Downloads tenant-generated skills into `.claude/skills/**/SKILL.md` (and `.cursor/rules`), tracked in `.moda/skills.yml`.

```bash theme={"dark"}
moda skills pull --status=proposed
```

| Flag       | Default    | Description                       |
| ---------- | ---------- | --------------------------------- |
| `--status` | `approved` | `approved`, `proposed`, or `all`. |

### moda skills sync

Pushes local `.claude/skills/**/SKILL.md` files up to Moda. `--dry-run` shows what would be pushed.

```bash theme={"dark"}
moda skills sync --dry-run
```

### moda skills proposals / moda skills proposal apply

`moda skills proposals list [--status=ready_for_pr]` lists skill improvement proposals with baseline/candidate pass rates. `moda skills proposal apply <proposal-id>` writes the proposed SKILL.md locally and acknowledges the proposal; it exits 1 if the local write succeeded but the acknowledgment failed (re-run to retry).

```bash theme={"dark"}
moda skills proposals list
```

## Harness

The harness commands produce and sync the cited map of your agent codebase. The supported path for analysis is the Moda GitHub App (see [Harness](/harness/overview)); the CLI analyze path is experimental.

### moda harness analyze

Produces a cited harness report. Gated: without `--experimental` or `MODA_HARNESS_ANALYZE_CLI=1` it prints a pointer to the GitHub App and exits 1.

```bash theme={"dark"}
moda harness analyze --json
```

```json Output (gated) theme={"dark"}
{
  "schema": "harness_analyze_disabled.v0.1",
  "status": "disabled",
  "reason": "cli_analyze_gated",
  "message": "Harness analysis now runs automatically via the Moda GitHub App — connect your repo in the Moda dashboard (Settings → Integrations). To run the experimental CLI analyze anyway, pass --experimental or set MODA_HARNESS_ANALYZE_CLI=1."
}
```

| Flag                                          | Description                                                                           |
| --------------------------------------------- | ------------------------------------------------------------------------------------- |
| `--experimental`                              | Opt in to CLI analysis (or set `MODA_HARNESS_ANALYZE_CLI=1`).                         |
| `--remote`                                    | Upload a safe source snapshot and analyze server-side (no local coding agent needed). |
| `--github-actions`                            | GitHub Actions OIDC mode — no Moda API key on the runner.                             |
| `--analyst=claude\|codex\|cursor\|local-scan` | Local analyst adapter.                                                                |
| `--yes`                                       | Skip the read-plan approval prompt.                                                   |

### moda harness pull

Fetches a server-side analyze run (status while running, the report when done). `--sync` also syncs a passing report. `--run-id` targets a specific run; otherwise the run recorded in `.moda/harness-remote-run.json` is used.

```bash theme={"dark"}
moda harness pull --sync
```

### moda harness validate-report / approve / sync

`validate-report` checks `.moda/harness-report.json` citations. `approve --yes` validates and writes a hash-bound approval file; `sync --from-report` uploads the approved report to Moda Cloud (it refuses unapproved reports) and writes `.moda/sync-state.json`. `moda sync` and `moda sync harness` are top-level aliases for harness sync; `moda sync prompts` aliases `moda prompts sync`.

```bash theme={"dark"}
moda harness approve --yes
moda harness sync --from-report .moda/harness-report.json
```

### moda harness scan / candidates / agents / explain

Local, no-LLM inspection commands:

* `moda harness scan` — discover local runtime agents and write `.moda/harness.json` (`--json` for structured output).
* `moda harness candidates` — static candidate pre-pass with file:line citations (`--out=PATH`).
* `moda harness agents` — list detected runtime agents and families.
* `moda harness explain [--agent=<id>]` — explain the discovered harness graph.

```bash theme={"dark"}
moda harness explain --agent=agent_support
```

### moda harness delete

Permanently deletes a synced harness from Moda Cloud.

```bash theme={"dark"}
moda harness delete my-harness-key --yes
```

## Agent-native commands

`moda agent context`, `moda agent investigate`, `moda agent next-action`, and `moda agent evidence <ref>` emit local Moda context, readiness diagnostics, prioritized next commands, and expanded evidence for coding agents. They default to agent JSON output in every environment. See [Using the CLI from agents and CI](/cli/agents).

```bash theme={"dark"}
moda agent next-action
```

## Next steps

* [Using the CLI from agents and CI](/cli/agents) — envelope schema, exit-code recipes, CI examples.
* [Data API overview](/data-api/overview) — the same data over HTTP.
* [Prompt management workflow](/prompt-management/workflow) — the full prompts loop these commands drive.
* [Harness CI rescan](/harness/ci-rescan) — the generated rescan workflow in detail.
