Skip to content
sonapersonal assistant
Channels

CLI

The CLI channel turns your terminal into a Sona client. Two modes:

  • REPL (default) — interactive prompt with JLine: arrow-history, slash commands, streaming token render.
  • JSON — newline-delimited JSON on stdin/stdout, suitable for pipelines, e2e smoke tests, and shell automation.

The terminal has no pairing handshake — this machine is the authority. Telegram (and other messengers) still go through pairing when you add them.

REPL mode

sona chat

This starts an in-process gateway from your config and opens a JLine prompt; it does not connect to a running Sona daemon. The prompt shows the active workspace, and the reply streams in as it arrives:

default > what time is it in istanbul?
The current time in Istanbul is 03:14 UTC+3 (assuming standard time).
default > /exit

Goodbye.

Built-in slash commands, handled by the REPL itself:

CommandEffect
/exit, /quitClose the REPL and stop its in-process gateway.
/clearANSI clear-screen. Arrow-key history is kept.
/helpList the REPL commands.
/workspace [name]List the workspaces, or switch to one.
/skill listList the registered skills.
/costToken usage and estimated spend for this REPL run.

Any other line that starts with / is matched against the gateway's skill registry, and a matching skill runs in the REPL; with no match the REPL prints Unknown command. Try /help. Everything else goes straight to the workspace Agent. It does not take the gateway's message path, so the REPL does not use pairing, profile or memory prompt context, or saved sessions.

Workspace selection

By default sona chat uses the default workspace. To pick a specific one:

sona chat --workspace personal

If the workspace name doesn't match, the REPL prints Unknown workspace '<name>', falling back to default. and continues in the default workspace (or the first workspace by name when there is no default). --workspace only affects the REPL; in JSON mode the gateway picks the workspace by channel, as it does for every other channel.

Streaming

REPL mode prints each chunk of the LLM reply as it arrives and ends the turn with a newline. Tool calls appear as inline lines while the turn runs. The REPL renders the chunks itself; it does not go through a channel adapter.

The framework's StreamingChannelAdapter path applies to JSON mode only: the gateway forwards the reply through CliChannel.sendChunk(UnifiedChunk.text(...)) and closes it with one UnifiedChunk.done(...). See How It Works for the SPI surface.

JSON mode

sona chat --json

JSON mode also starts an in-process gateway, but unlike the REPL it registers the framework's CliChannel with it, so every line takes the gateway's message path. The wire format is newline-delimited JSON in both directions. Useful for piping into Sona from another program, recording golden transcripts in tests, or driving Sona from a script without an interactive terminal.

Input

One JSON object per line, each with at least a text field:

{"text": "hi"}
{"text": "what time is it in istanbul?"}

Other fields on the object are reserved for future use; today they're ignored.

Output

Streaming responses come back as a sequence of chunk records, terminated by a done: true envelope:

{"type":"chunk","content":"Hello","done":false}
{"type":"chunk","content":" ","done":false}
{"type":"chunk","content":"world","done":false}
{"type":"chunk","content":"","done":true}

Pipeline-friendly consumers can stream the body by concatenating content fields until done: true; one-shot consumers can buffer until done then read the response in one go.

Errors come back as a single error record:

{"type":"error","message":"invalid JSON: Unexpected character (' ')..."}

Sona writes one record per inbound message; a single sona chat --json invocation can handle multiple turns in the same conversation by keeping the process open and piping more text lines in.

Example: shell pipeline

printf '%s\n' \
  '{"text":"hi"}' \
  '{"text":"summarise the TnsAI framework in one paragraph"}' \
  | sona chat --json \
  | jq -r 'select(.type=="chunk") | .content' \
  | tr -d '\n'

This streams two turns through Sona, extracts the content chunks, and strips chunk-boundary newlines so you get the assistant's reply as flowing text.

Sessions

Only JSON mode saves session history. sona chat --json goes through the gateway as (channelId=cli, senderId=<your-username>), so later --json runs in the same workspace load that saved history into their session while the file is still on disk. That history is not replayed into the Agent's LLM memory; see How It Works. The gateway does add the sleep-time MEMORY.md and USER.md summaries for your scoped ID to each turn's prompt. The interactive REPL calls the Agent directly and saves no session history.

Session files are named by the URL-safe Base64 of the workspace and scoped ID, so there is no readable per-user filename. To start over, stop the daemon first (sona stop), because a running daemon saves its open sessions again after the next turn. Then remove the saved sessions (this clears every session on the machine, and it does not clear the sleep-time summaries under ~/.sona/memory/):

rm ~/.sona/sessions/*.json

A /new slash command is tracked as a future improvement.

When to use which mode

Use caseMode
Quick interactive questionREPL
Pair-programming with Sona, multi-turnREPL
Shell pipeline / xargs over a CSVJSON
E2E test that asserts response shapeJSON
Recording a golden transcript for a regression suiteJSON
Driving Sona from another program (Python script, Node, …)JSON

JSON mode never prints prompts, banners, ANSI colour, or anything other than JSON records — safe to pipe.

Comparison to Telegram

TelegramCLI
AuthPairing code + owner approvalLocal user (no handshake)
StreamingNo (the reply is sent once the turn completes)Yes (REPL renders chunks itself) / Yes (JSON, per-chunk records)
MediaPhotos, documents, voice (inbound)No
ThreadsYes (per-chat threading)REPL: no saved session. JSON: one session per workspace and lowercased user.name
Use casePhone / off-machineLocal dev, CI smoke tests, scripts
LatencyNetwork round-trip via Telegram serversDirect stdin/stdout

The two adapters are independent. You can run both at the same time — they're separate sessions even for "the same" person.