# @superdoc/cli

LLM-first CLI for deterministic DOCX operations through SuperDoc's Document API.

## Install

```bash
npm install -g @superdoc/cli
```

The package automatically installs a native binary for your platform via optionalDependencies. Supported platforms:

| Platform | Package |
|----------|---------|
| macOS (Apple Silicon) | `@superdoc/cli-darwin-arm64` |
| macOS (Intel) | `@superdoc/cli-darwin-x64` |
| Linux (x64) | `@superdoc/cli-linux-x64` |
| Linux (ARM64) | `@superdoc/cli-linux-arm64` |
| Windows (x64) | `@superdoc/cli-windows-x64` |

## Usage

```bash
superdoc <command> [options]
```

## Getting Started

Stateful editing flow (recommended for multi-step edits):

```bash
superdoc open ./contract.docx

# Use query match to find a mutation-grade target
superdoc query match --select-json '{"type":"text","pattern":"termination"}' --require exactlyOne

# Mutate using the returned target
superdoc replace --target-json '{"kind":"text","blockId":"p1","range":{"start":0,"end":11}}' --text "expiration"

superdoc save --in-place
superdoc close
```

## Encrypted Documents

Open password-protected `.docx` files with `--password` or the `SUPERDOC_DOC_PASSWORD` env var:

```bash
# Explicit flag
superdoc open ./secret.docx --password 'mypassword'

# Env var (preferred — avoids password in process listings)
SUPERDOC_DOC_PASSWORD='mypassword' superdoc open ./secret.docx

# Via call
superdoc call doc.open --input-json '{"doc":"./secret.docx","password":"mypassword"}'
```

If the password is missing or incorrect, the CLI returns a structured error with one of these codes:
- `DOCX_PASSWORD_REQUIRED` — encrypted file, no password supplied
- `DOCX_PASSWORD_INVALID` — wrong password
- `DOCX_ENCRYPTION_UNSUPPORTED` — recognized but unsupported encryption method
- `DOCX_DECRYPTION_FAILED` — crypto failure or corrupt data

## Choosing the Right Command

Search the CLI contract when you know the intent but not the operation name:

```bash
superdoc discover "create a footnote"
superdoc discover "add an sdt"
superdoc discover "change table cell padding"
```

`discover` uses the generated Document API contract plus CLI vocabulary and operation relationships. It returns an adaptive relevant set with at most ten results. The leading result includes a compact input shape, CLI parameters, a command template, and the exact `describe command` follow-up. Use `--limit <1-10>` to request a smaller ceiling.

### Which command should I use?

| I want to... | Use this command |
|--------------|------------------|
| Find a mutation target (block ID, text range) | `query match` |
| Search/browse document content | `find` |
| Insert inline text within a block | `insert` |
| Create a new standalone paragraph | `create paragraph` |
| Create a new heading | `create heading` |
| Insert a list item before/after another list item | `lists insert` |
| Apply formatting to a text range | `format apply` or format helpers (`format bold`, etc.) |
| Apply multiple changes in one operation | `mutations apply` |

### Mutation targeting workflow

Always use `query match` to discover targets before mutating:

```bash
# Step 1: Find the target
superdoc query match --select-json '{"type":"text","pattern":"Introduction"}' --require exactlyOne

# Step 2: Use the returned address in a mutation
superdoc replace --block-id <returned-blockId> --start <start> --end <end> --text "Overview"
```

`find` is for content discovery and inspection. `query match` is for mutation targeting — it returns exact addresses with cardinality guarantees.

### Block-oriented editing workflow

Use `blocks list` for ordered inspection, then `blocks delete-range` for contiguous removal:

```bash
superdoc open ./contract.docx

# 1. Inspect block order, IDs, and text previews
superdoc blocks list --limit 30

# 2. Preview the deletion (no mutation, shows what would be removed)
superdoc blocks delete-range \
  --start-json '{"kind":"block","nodeType":"paragraph","nodeId":"abc123"}' \
  --end-json '{"kind":"block","nodeType":"paragraph","nodeId":"def456"}' \
  --dry-run

# 3. Apply the deletion
superdoc blocks delete-range \
  --start-json '{"kind":"block","nodeType":"paragraph","nodeId":"abc123"}' \
  --end-json '{"kind":"block","nodeType":"paragraph","nodeId":"def456"}'

superdoc save --in-place
```

This replaces the pattern of calling `blocks delete` once per block. A 17-block removal becomes one command.

### Preview-before-apply workflow

Use `--dry-run` and `--expected-revision` for safe, auditable mutations:

```bash
superdoc open ./contract.docx

# 1. Check session state
superdoc status

# 2. Find the mutation target
superdoc query match --select-json '{"type":"text","pattern":"termination"}' --require exactlyOne

# 3. Preview the change (validates input, shows what would change, no mutation)
superdoc replace --block-id p1 --start 0 --end 11 --text "expiration" --dry-run

# 4. Apply with revision guard (fails if document changed since preview)
superdoc replace --block-id p1 --start 0 --end 11 --text "expiration" --expected-revision 1

superdoc save --in-place
```

### Common mistakes

1. **Do not use `find` output to construct mutation targets.** `find` returns discovery-grade data, not mutation-grade addresses. Use `query match` instead.
2. **Do not use `insert --block-id` for sibling block insertion.** `insert` inserts inline text *within* a block. To create a new block adjacent to another, use `create paragraph`, `create heading`, or `lists insert`.
3. **Do not use `create paragraph` to continue a list.** If you want to add a list item adjacent to existing list items, use `lists insert`. `create paragraph` creates a standalone (non-list) paragraph.

## Command Index

| Category | Commands |
|----------|----------|
| query | `find`, `query match`, `get-node`, `get-node-by-id`, `get-text`, `info` |
| mutation | `insert`, `replace`, `delete`, `blocks delete`, `blocks delete-range`, `blocks list`, `mutations apply`, `mutations preview` |
| format | `format apply`, `format bold`, `format italic`, `format underline`, `format strikethrough` |
| create | `create paragraph`, `create heading`, `create table-of-contents` |
| lists | `lists list`, `lists get`, `lists insert`, `lists create`, `lists attach`, `lists detach`, `lists join`, `lists separate`, `lists set-level`, `lists indent`, `lists outdent`, `lists set-value`, `lists set-type`, `lists convert-to-text` |
| comments | `comments add`, `comments reply`, `comments delete`, `comments get`, `comments list` |
| trackChanges | `track-changes list`, `track-changes get`, `track-changes accept`, `track-changes reject`, `track-changes accept-all`, `track-changes reject-all` |
| history | `history get`, `history undo`, `history redo` |
| lifecycle | `open`, `save`, `close` |
| session | `session list`, `session save`, `session close`, `session set-default`, `session use` |
| introspection | `status`, `discover`, `describe`, `describe command` |
| low-level | `call <operationId>` |
| legacy compat | `search`, `replace-legacy <find> <to> <files...>`, `read` |

For full command help and examples, run:

```bash
superdoc --help
superdoc discover "<natural-language intent>"
superdoc save --help
superdoc create paragraph --help
superdoc describe command <command-name>
superdoc describe command doc.save
```

Use `--help` or `-h` with a command to read its usage without opening a document or executing the command. Operation help includes `schemaCommand`, an executable command for retrieving the complete parameter schema. Add `--pretty` for terminal-readable output.

## v1 Breaking Changes

This CLI replaces the previous `@superdoc-dev/cli` package surface with the v1 contract-driven command set.

| Legacy command | v1 status | Migration |
|---------------|-----------|-----------|
| `superdoc replace <find> <to> <files...>` | Renamed to `replace-legacy` | Use `replace-legacy`, or use `query match` + `replace --target-json` for the v1 workflow. |

Legacy compatibility is retained for `search`, `read`, and `replace-legacy`.

## Normative Policy

- Canonical contract/version metadata comes from `@superdoc/document-api` (`CONTRACT_VERSION`, operation metadata, and schemas).
- This README is usage guidance for CLI consumers.
- If guidance here conflicts with `superdoc describe`/`describe command` output or document-api contract exports, those are authoritative.

## Host mode (stdio JSON-RPC)

```bash
superdoc host --stdio
```

- Starts a persistent JSON-RPC 2.0 host over newline-delimited stdio frames.
- Intended for SDK/runtime integrations that need long-lived command execution in a single process.
- Supported methods:
  - `host.ping`
  - `host.capabilities`
  - `host.describe`
  - `host.describe.command` (requires `params.operationId`)
  - `host.shutdown`
  - `cli.invoke` (executes canonical CLI command semantics)

## API introspection commands

```bash
superdoc discover "set padding on one table cell"
superdoc describe
superdoc describe command doc.find
superdoc status
```

- `discover <intent>` ranks relevant operations and expands the leading input shape without opening a document.
- `describe` returns contract + protocol metadata and the exhaustive operation catalog.
- `describe command <operationId>` returns one operation definition (inputs, response schema, errors, examples).
- `status` shows current session status and document metadata.

## Discovery evaluation

The repository includes a canonical intent corpus with expected operations in `eval/discovery-cases.json`.

```bash
pnpm eval:discovery
pnpm eval:discovery -- --case sdt-ambiguous-add
pnpm eval:discovery -- --query "add a repeating section control"
pnpm eval:discovery -- --json
```

The suite reports Top-1 accuracy, MRR, Recall@3, Recall@10, mean result count, payload size, and p95 search latency. It exits nonzero when an expected ranking, result-set bound, command helper, or ambiguity classification fails.

## Stateful session commands

```bash
superdoc open ./contract.docx
superdoc status
superdoc query match --select-json '{"type":"text","pattern":"termination"}' --require exactlyOne
superdoc replace --target-json '{...}' --text "Updated clause"
superdoc save --in-place
superdoc close
```

- `open` creates a new session id automatically unless `--session <id>` is provided.
- If `<doc>` is omitted, commands run against the active default session.
- Explicit `<doc>` (or `--doc`) always runs in stateless mode and does not use session state.

## Session management

```bash
superdoc session list
superdoc session save <sessionId> [--in-place] [--out <path>] [--force]
superdoc session set-default <sessionId>
superdoc session use <sessionId>
superdoc session close <sessionId> [--discard]
```

## Read / locate commands

```bash
superdoc info [<doc>]
superdoc find [<doc>] --type text --pattern "termination" --limit 5
superdoc query match [<doc>] --select-json '{"type":"text","pattern":"termination"}' --require exactlyOne
superdoc get-node [<doc>] --address-json '{"kind":"block","nodeType":"paragraph","nodeId":"p1"}'
superdoc get-node-by-id [<doc>] --id p1 --node-type paragraph
```

- `find` returns discovery-grade results for content search and browsing.
- `query match` returns mutation-grade addresses and text ranges — use this before any mutation.
- For text queries, use the returned `blocks[].range` as targets for `replace`, `comments add`, and formatting commands.

## Mutating commands

```bash
superdoc replace [<doc>] --target-json '{...}' --text "Updated text" [--out ./updated.docx]
superdoc insert [<doc>] --value "New text" [--out ./inserted.docx]
superdoc blocks delete [<doc>] --node-type paragraph --node-id abc123
superdoc blocks delete-range --start-json '{"kind":"block",...}' --end-json '{"kind":"block",...}'
superdoc create paragraph [<doc>] --text "New paragraph" [--at-json '{"kind":"after","target":{"kind":"block","nodeType":"paragraph","nodeId":"p1"}}']
superdoc lists insert [<doc>] --node-id li1 --position after --text "New item"
superdoc format bold [<doc>] --target-json '{...}' [--out ./bolded.docx]
superdoc comments add [<doc>] --block-id p1 --start 0 --end 10 --text "Please revise" [--out ./with-comment.docx]
```

- In stateless mode (`<doc>` provided), mutating commands require `--out`.
- In stateful mode (after `open`), mutating commands update the active working document and `--out` is optional.
- Use `--dry-run` to preview any mutation without applying it.
- Use `--expected-revision <n>` with stateful mutating commands for optimistic concurrency checks.

## Block inspection

```bash
superdoc blocks list
superdoc blocks list --limit 20 --offset 10
superdoc blocks list --node-types-json '["paragraph","heading"]'
```

- Returns ordered block metadata: ordinal, nodeId, nodeType, textPreview, isEmpty.
- Use the returned nodeIds as targets for `blocks delete`, `blocks delete-range`, or other block-oriented commands.

## Low-level invocation

```bash
superdoc call <operationId> --input-json '{...}'
```

- Invokes any document-api operation directly with a JSON payload.

## Save command modes

```bash
superdoc save --in-place
superdoc save --out ./final.docx
```

- `save` persists the active session but keeps it open for more edits.
- If no source path exists (for example stdin-opened docs), `save` requires `--out <path>`.
- `save --in-place` checks for source-file drift and refuses overwrite unless `--force` is passed.

## Close command modes

```bash
superdoc close
superdoc close --discard
```

- Dirty contexts require explicit `--discard` (or run `save` first, then `close`).

## Output modes

- Default: `--output json` (machine-oriented envelope)
- Human mode: `--output pretty` (or `--pretty`)

```bash
superdoc info ./contract.docx --output json
superdoc info ./contract.docx --pretty
```

## Global flags

- `--output <json|pretty>`
- `--json`
- `--pretty`
- `--session <id>`
- `--timeout-ms <n>`
- `--help`
- `--version`, `-v`

## Input payload flags

- `--query-json`, `--query-file` (`find`, `lists list`)
- `--address-json`, `--address-file` (`get-node`, `lists get`)
- `--target-json` (mutation commands — no `--target-file` counterpart; use flat flags `--block-id`/`--start`/`--end` as alternative)
- `--input-json`, `--input-file` (`call`, `create paragraph`)
- `--at-json`, `--at-file` (`create paragraph`)

## Stdin support

Use `-` as `<doc>` to read DOCX bytes from stdin:

```bash
cat ./contract.docx | superdoc open -
cat ./contract.docx | superdoc info -
```

## JSON envelope contract

Normative operation/version metadata comes from `@superdoc/document-api`; use `superdoc describe` for the runtime contract surface.

Success:

```json
{
  "ok": true,
  "command": "find",
  "data": {},
  "meta": {
    "version": "1.0.0",
    "elapsedMs": 42
  }
}
```

Error:

```json
{
  "ok": false,
  "error": {
    "code": "INVALID_TARGET",
    "message": "Expected paragraph:abc123 but found listItem:abc123.",
    "details": {
      "requestedNodeType": "paragraph",
      "actualNodeType": "listItem",
      "nodeId": "abc123",
      "remediation": "Use lists.insert to add an item to a list sequence."
    }
  },
  "meta": {
    "version": "1.0.0",
    "elapsedMs": 8
  }
}
```

## Part of SuperDoc

This CLI is part of [SuperDoc](https://github.com/superdoc/docx-editor) — open-source DOCX editing and tooling. Renders, edits, and automates .docx in the browser and on the server.

## License

AGPL-3.0 · [Enterprise license available](https://superdoc.dev)
