TnsAI
Sona

Machine-readable mode

Every non-streaming sona command accepts --json and emits a single-line envelope. Streaming commands (chat) emit one envelope per chunk (ndJSON). This is the surface you wire orchestrators, CI pipelines, and parent agents against — no scraping of human text, no fragile regex.

Envelope shape

One of two shapes, picked by success or failure:

{ "ok": true,  "command": "status",   "result": { "running": true, "pid": 12345 } }
{ "ok": false, "command": "uninstall", "error": { "code": "confirmation_required", "message": "uninstall is destructive; --json requires explicit --yes to skip the prompt." } }

Fields:

  • ok (boolean) — true on success, false on error.
  • command (string) — the subcommand that wrote the envelope (status, update, uninstall, …). Matches what you typed after sona.
  • result (object, success only) — command-specific payload. See the table below for the per-command shape.
  • error.code (string, error only) — machine-readable identifier. Stable across releases.
  • error.message (string, error only) — human-readable explanation. May change between releases; do not match against it.

Exit code reflects the same semantics: 0 for success, non-zero for errors. The exit code is the orchestrator's primary signal; the envelope is the explanation.

Commands

CommandSuccess result keysNotable error codes
sona --version --jsonversion, jre
sona --help --jsonversion, commands[], globalOptions[]
sona status --jsonrunning, pid, reason— (exit 1 when not running)
sona stop --jsonstopped, pid, used_forcesignal_rejected, timeout, interrupted
sona init --jsonconfigExists, configPathinteractive_required (with --add-channel)
sona update --jsonaction (one of installed / already_latest), from, to, currentendpoint_unusable, no_download_url, install_failed
sona update --check --jsonaction: "check", current, latest, url, upToDateendpoint_unusable
sona update --rollback --jsonaction: "rolled_back"no_previous_version, rollback_failed
sona uninstall --dry-run --jsondryRun: true, paths[]
sona uninstall --yes --jsondryRun: false, removed, failed, skipped, removedPaths[], failedPaths[]confirmation_required, daemon_stop_failed
sona doctor --jsonschemaVersion, overall (one of ok / warn / fail), checks[]— (own document, not the envelope)
sona logs --count --jsoncount
sona chat --jsonper-chunk records (ndJSON; see CLI channel)
sona search --json "<q>"query, total, hits[]
sona config show --jsonmasked sona.yml as a config map
sona cost --json [WINDOW]window, totalUsd, byProvider, byWorkspace

doctor --json predates the envelope contract (TNS-466 axis 2). It emits its own document with schemaVersion, overall, and checks. The shape is stable; do not assume the standard envelope.

Examples

Health check in a CI script

if ! sona doctor --json | jq -e '.overall == "ok"' > /dev/null; then
  echo "Sona not healthy; aborting deploy"
  exit 1
fi

Upgrade only when a newer version exists

LATEST=$(sona update --check --json | jq -r '.result.latest')
CURRENT=$(sona update --check --json | jq -r '.result.current')
if [ "$LATEST" != "$CURRENT" ]; then
  sona update --json | jq .
fi

Audit what uninstall would touch

sona uninstall --dry-run --json | jq '.result.paths[]'
# → "/Users/me/.sona"
# → "/Users/me/.local/bin/sona"
# → "/Users/me/.local/share/sona"

Read config state without triggering the wizard

sona init --json | jq '{exists: .result.configExists, path: .result.configPath}'
# → { "exists": true, "path": "/Users/me/.sona/sona.yml" }

init --json is a query. The interactive wizard is never triggered; only configExists + configPath are reported. To bootstrap from scratch, fall back to interactive sona init and let the operator walk the prompts. init --add-channel --json is rejected with interactive_required for the same reason — the channel walkthrough is multi-prompt and doesn't translate to a single envelope.

Destructive commands and --json

sona uninstall is destructive. In --json mode, the prompt would be silently skipped; we refuse rather than allow that. --json alone returns confirmation_required (exit 1):

{ "ok": false, "command": "uninstall", "error": { "code": "confirmation_required", "message": "..." } }

Pass --yes explicitly to opt in:

sona uninstall --yes --json

--dry-run --json is always safe (no filesystem touch) and does not need --yes.

Streaming vs. one-shot

This page covers one-shot commands. Streaming commands (sona chat --json) emit ndJSON — one envelope per chunk, terminated by a done: true record. See the CLI channel page for the per-chunk shape and a worked example.

Stability

  • ok, command, result/error keys: stable. Renaming would break every orchestrator. Treated as semver-major.
  • error.code values: stable within a minor release. New codes may appear in minor releases; existing codes never change spelling.
  • result payload keys per command: additive. New keys may appear; existing keys keep their meaning.
  • error.message text: unstable. Read for humans; never match against.

See Sona how-it-works for the architecture context, or the TnsAI.Sona release notes for the per-version changelog.

On this page