# CLI

> Every subcommand and alias, how a notebook path resolves, exit behaviour, and the exact grammar of the success and error reports, their trailers, and their colour.

Canonical URL: https://www.slivingdoc.dev/docs/reference/cli/ · Version: 0.1 · Updated: 2026-09-21

Full site index: https://www.slivingdoc.dev/llms.txt

slivingdoc is a subcommand CLI. The subcommand comes first, before any
flag.

## Subcommands

| Command   | Alias | Effect                                                        |
| --------- | ----- | ------------------------------------------------------------- |
| `serve`   | `s`   | Serve the notebook over MCP stdio. This is the server.        |
| `pull`    | `p`   | Write the current notebook into a directory and exit.         |
| `commit`  | `c`   | Publish the changes at a directory (`-m <message>`) and exit. |
| `version` | `v`   | Print `slivingdoc <semver>` and exit, touching nothing else.  |

A missing or unknown command writes the command listing to stdout and
exits nonzero; no server starts.

`serve`, `pull`, and `commit` read the same flags and environment
variables, and `-h` on any of them prints the same reference to stdout.
`commit` adds the required `-m`/`--message` flag. See
[Configuration](/docs/reference/configuration/) for the full flag table.

`slivingdoc version` writes `slivingdoc <semver>` and one LF to stdout and
exits zero without resolving configuration or loading the native and S3
dependencies. `-h` on `serve`, `pull`, or `commit` follows the same rule.

## Arguments and path resolution

`pull` and `commit` take at most one positional notebook path, which may
precede or follow the flags. Resolution is:

1. An omitted path uses the workspace root, which is the working
   directory unless `--workspace-root` says otherwise.
2. A path beginning with `~/` resolves against the current user's home
   directory.
3. Any other relative path resolves against the working directory.
4. The resolved path must stay at or below the workspace root.

`--` ends flag parsing, as it does for any Go flag set. A second
positional argument is an error: "at most one notebook path argument is
accepted".

`pull` and `commit` never take a session directory. They address a
directory you can still open after the process exits. `serve` with
neither root configured does take one — see
[Share a directory with humans](/docs/guides/shared-directory/).

## Exit behaviour

| Outcome                                                                                      | Stream                          | Exit                                                        |
| -------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------------- |
| Success                                                                                      | stdout                          | zero                                                        |
| Domain error (the report below)                                                              | stdout                          | nonzero                                                     |
| Argument refusal: a missing `-m`, or more than one path                                      | stderr                          | nonzero, before any native or network dependency is touched |
| Configuration refusal, including an invalid flag value or a path named by both path settings | stderr, one redacted diagnostic | nonzero                                                     |
| Startup probe failure (`INCOMPATIBLE_STORE`)                                                 | stderr, redacted                | nonzero, before any tool call                               |
| Missing or unknown command                                                                   | stdout (the listing)            | nonzero                                                     |

`pull` and `commit` run the same startup sequence as `serve` — the pinned
engine check and the S3 compatibility probe — then perform one operation
and exit. Logs always go to stderr; stdout carries only protocol messages
and command output.

`serve` stops on SIGINT and SIGTERM: it accepts no new requests, cancels
in-flight request contexts, waits at most 30 seconds, closes native and
lock resources, and exits nonzero if that deadline expires.

## The success report

A successful subcommand writes the unified result report to stdout and
exits zero. Its shape is a status line, one indented line per changed
file, a totals trailer, and the path-set trailers:

```text
OK  generation 18  /srv/notes
  archive/old.md  -3
  notes/a.md  +1 -1
  notes/c.md  +2
3 files changed, 3 insertions(+), 4 deletions(-)
```

| Part           | Grammar                                                                                                                                         |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Status line    | The `OK` token, two spaces, `generation <n>` (the accepted remote generation after the operation), two spaces, the resolved notebook directory. |
| File line      | Two spaces, the path, two spaces, then `+<insertions>` and `-<deletions>`, separated by one space. A zero-count side is omitted entirely.       |
| Totals trailer | `<n> files changed, <n> insertions(+), <n> deletions(-)`.                                                                                       |
| Path trailers  | The trailers below, when configured.                                                                                                            |

The diffstat answers "what is new to check out". `pull` reports the delta
between the visible directory before the pull and the materialised
result, so a file restored under a read-only path shows up there like any
other change. `commit` reports the increment the publication added over
the remote state it observed. A no-op synchronisation reports an empty
stat: no file lines, and zeros in the totals.

A diffstat line is an LF-terminated run of bytes, with one trailing CR
stripped for comparison and counting. A final run without a trailing LF
still counts as one line; content ending in LF has no phantom empty final
line; empty content has zero lines. A file present only after the change
counts every line as an insertion, a file present only before it counts
every line as a deletion, and a modified file uses a deterministic line
diff.

> **Note:** The example in the tool's own operator reference abbreviates
> this report: it omits the resolved directory on the status line. The
> grammar above is what the binary prints.

## The error report

A domain error prints the same status/detail/trailer skeleton to stdout
and exits nonzero:

```text
CONTENT_CONFLICT · MERGE_CONFLICT
Resolve the conflict blocks before notes_commit.
  notes/today.md  conflict  lines 12-18, 40-42
next: edit the files, then commit
retryable: false
```

| Part          | Grammar                                                                                                                                                                                                                             |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Status line   | The error code, a space, a middle dot, a space, then the reason token.                                                                                                                                                              |
| Message       | One line. Message text can change between releases; the tokens cannot.                                                                                                                                                              |
| File line     | Two spaces, the path padded to the report's longest path plus two spaces, the file reason as lower-case words, then `lines <start>-<end>` pairs joined by `, ` when the file has marker ranges. Ranges are one-based and inclusive. |
| `next:`       | The caller's next step, worded from the `action` token.                                                                                                                                                                             |
| `retryable:`  | `true` or `false`.                                                                                                                                                                                                                  |
| `recovery:`   | `stage=<stage> remoteAccepted=<yes\|no\|unknown> resynchronized=<bool>`, only for `RECOVERY_FAILURE`.                                                                                                                               |
| Path trailers | The trailers below, when configured.                                                                                                                                                                                                |

File reasons are rendered as words:

| Token                | Printed as           |
| -------------------- | -------------------- |
| `TEXT_CONFLICT`      | `conflict`           |
| `PATH_CONFLICT`      | `path conflict`      |
| `UNRESOLVED_MARKERS` | `unresolved markers` |
| `READ_ONLY`          | `read-only`          |
| `INVALID_CONTENT`    | `invalid content`    |

And so is the action token, on the `next:` line:

| Token        | Printed as                             |
| ------------ | -------------------------------------- |
| `FIX_INPUT`  | `correct the request, then call again` |
| `EDIT_FILES` | `edit the files, then commit`          |
| `PULL`       | `pull, then continue`                  |
| `RETRY`      | `retry the same call`                  |
| `OPERATOR`   | `operator attention needed`            |

An unknown token of either kind is printed verbatim rather than dropped.
[Errors](/docs/reference/errors/) lists every code, reason, and action.

## Path-set trailers

Both reports end with the same trailers, each printed only when that set
is configured:

```text
writable: agents/scout
read-only: docs, faq.md
path-rule: longest match decides
```

The writable trailer precedes the read-only one, because the writable set
is the frame and the read-only entries are the exceptions inside it. The
`path-rule:` line appears only when both sets are non-empty, since the
two can then name the same region at different depths. Entries are
normalised, sorted, and joined with `, `. See
[Restrict agents with path policies](/docs/guides/path-policies/).

A read-only refusal therefore reads:

```text
INVALID_REQUEST · READ_ONLY_PATH
docs is read-only in this server. Your changes there were discarded and the files reset. Write outside the read-only paths, then commit again.
  docs/faq.md  read-only
next: edit the files, then commit
retryable: false
read-only: docs
```

## Colour

Colour is presentation-only. The `OK` token is green, the generation
summary cyan, per-file insertion counts green and deletion counts red;
on an error report the code is red, the reason token dim, a file path
yellow, the file reason word dim, `next:` cyan, and the three trailer
labels dim.

Colour appears only when stdout is a real terminal: piped or redirected
output stays plain text, and any non-empty `NO_COLOR` disables it even on
a terminal. Stripped of its escape sequences, a coloured report is
byte-identical to the plain one, and a success report always starts with
the `OK` token, so scripts can match on it. See
[Logging and profiling](/docs/reference/logging/) for `NO_COLOR`'s effect
on log records.

## Next

- [Use the CLI without a host](/docs/guides/cli/) — the same commands in
  practice, including from scripts.
- [MCP tools](/docs/reference/mcp-tools/) — the same two operations over
  MCP, with their structured results.
- [Configuration](/docs/reference/configuration/) — every flag and
  environment variable.
