Use the CLI without a host

Pull and commit by hand, how a notebook path resolves, how to read the success and error reports, and how to drive both from scripts.

pull and commit are the human mirror of the two MCP tools. They take the same flags as serve, run the same startup sequence, perform one operation, print the result, and exit. Startup is the pinned engine check and then the store check: the S3 compatibility probe (skipped when a recent proof is on record, see Set up a bucket), or an access check against hosted storage. No MCP host is involved, and no daemon is left behind.

export SLIVINGDOC_BUCKET=my-notes
slivingdoc pull notes
# edit UTF-8 text files under notes/
slivingdoc commit notes -m "meeting summary"

That is the whole loop. pull writes the current notebook into the directory. commit publishes what you changed there and merges in any concurrent, non-conflicting changes other writers published meanwhile.

With hosted storage there are two ways to authenticate, and neither needs AWS settings:

# on your own machine: log in once through the browser
slivingdoc login
slivingdoc space notes        # only needed to pick a default space
slivingdoc pull notes

# in CI or any environment without a browser
export SLIVINGDOC_TOKEN=<your-api-token>
slivingdoc pull notes

slivingdoc login stores an account login on this machine, and slivingdoc logout revokes and removes it. slivingdoc space lists the spaces the login reaches, and slivingdoc space <name> sets the default one. A token reaches exactly one space, which it names itself; a stored login uses --space, then SLIVINGDOC_SPACE, then the default space. --storage auto|hosted|s3 (or SLIVINGDOC_STORAGE) forces the choice. Under the default auto, a token beside --endpoint, AWS_ENDPOINT_URL or AWS_ENDPOINT_URL_S3 is refused as ambiguous; pass --storage hosted to say the endpoint is the hosted one. The token needs slivingdoc 0.2.2 or newer, and the stored login 0.2.4 or newer. See Use hosted storage and Configuration.

The first pull into a directory is guarded. If the directory already holds files, each one must be in the notebook with identical bytes, or the notebook must be empty (the files then seed it). Anything else is refused as INVALID_REQUEST with the reason DIRECTORY_NOT_EMPTY, and nothing is changed. Pull into a new or empty directory first.

The notebook path

Each subcommand takes at most one notebook path. It may come before or after the flags.

  • Omitting it uses the workspace root, which is the working directory unless --workspace-root says otherwise.
  • A path that begins with ~/ resolves against the current user’s home directory.
  • Any other relative path resolves against the working directory.
  • The resolved path must stay at or below the workspace root.

commit also requires a message, -m or --message. A missing message, or more than one path, exits nonzero before any native or network dependency is touched — so a typo in a script fails fast and costs nothing.

The success report

A subcommand that succeeds writes its report to standard output and exits zero:

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

Line by line:

  • OK is the status token, generation 18 is the accepted remote generation the operation ended on, and the last field is the notebook directory the operation worked in.
  • One line per changed file, with its insertion and deletion counts. A zero count is left out, so -3 means deletions only.
  • The totals trailer closes the report.

The per-file counts answer “what is new to check out”. For pull they are the delta between the directory as it was and the materialized result. For commit they are the increment your publication added over the remote state it observed. A synchronization that changed nothing reports an empty stat.

The error report

A domain error prints the same skeleton to standard output, prints error: <CODE> on standard error, and exits 1:

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
  • The status line is the error code, a middle dot, and the reason token.
  • Then the message.
  • Then one line per affected file: its reason in lower-case words, and its one-based inclusive line ranges when the reason has them.
  • next: names your next step, and retryable: says whether trying again unchanged can help.
  • A recovery report follows when the operation performed one.

Both reports end with the same path-policy trailers when the process is configured with them: a writable: trailer naming the configured writable set, then a read-only: trailer naming the configured read-only set, and — when both are set — a path-rule: longest match decides trailer, because the two sets can name the same region at different depths. See Restrict agents with path policies.

Errors lists every code, reason, and action. CONTENT_CONFLICT has its own guide: Resolve conflicts.

On a terminal

A terminal gets extra presentation; a script gets none of it.

  • While pull or commit runs, standard error shows a progress line with a spinner, for example Pulling /home/me/notes · space notes · 1.2s. The verb is Pulling or Publishing, then the resolved path, then the space or bucket. It is cleared when the operation ends. Standard output never carries it.
  • The report is coloured, and the status line gains a mark: ✓ before OK, ✗ before an error, and → in place of next:.
  • A refusal at startup is one line on standard error, error: <message> in a script and ✗ <message> on a terminal.

Standard output and standard error are each judged on their own. Piped or redirected output is plain text without marks or escape codes, so the lines a script reads (OK generation 18 <dir>, next:) are stable. Any non-empty NO_COLOR disables the colour even on a terminal.

From scripts and cron

Nothing about the two commands is interactive, so a script can drive them directly:

  • Read the exit status. Zero is success. Every failure exits 1: a domain error leaves its report on standard output, and a startup refusal leaves standard output empty and one error: line on standard error.
  • Redirected output is already plain text. Set NO_COLOR as well if the job may run attached to a terminal.
  • Standard output carries only the report. Logs go to standard error, where LOG_LEVEL controls them. See Logging and profiling.
  • Configure the job through the environment (SLIVINGDOC_BUCKET, AWS_REGION, and the rest) instead of a long flag list. Flags override environment variables, and the environment overrides the defaults. Prefer SLIVINGDOC_TOKEN for hosted storage in a job; the stored login is meant for a person’s own machine.

Warning: serve resolves the AWS credential chain once and holds the session, but every pull or commit invocation resolves it fresh. With short-lived STS or SSO credentials, each scheduled run needs a currently valid session. The recorded probe proof does not bind credentials, so an expired session is refused by the first request the run makes, not at startup.

Next

Last updated September 29, 2026

Type to search the documentation.