TnsAI

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.

Unlike Telegram, the CLI channel has no auth handshake — your terminal is the authority. If you don't want the keys to your daemon in ~/.sona/, don't run sona chat.

REPL mode

sona chat

This opens a JLine session against your local daemon. You'll see the user name prompt and a streaming reply:

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

Built-in slash commands (intercepted locally by the REPL — they never reach the gateway):

CommandEffect
/exit, /quitClose the REPL, return to your shell. The daemon keeps running.
/clearANSI clear-screen. History is preserved.

Anything else flows to the gateway as a UnifiedMessage — including text that starts with /, in case you've registered a skill that matches a slash prefix.

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 CLI prints the list of available workspaces and exits.

Streaming

REPL mode streams the LLM reply token-by-token as it arrives. There is one assistant: prefix per turn, then the deltas concatenate inline, then a newline closes the line. No per-token re-render, no flicker — the assistant prefix is emitted once on the first chunk and the cursor walks forward.

Internally this uses the framework's StreamingChannelAdapter mixin (TNS-440); the gateway routes every chunk through sendChunk(UnifiedChunk.text(...)) and a final UnifiedChunk.done(...). See How It Works for the SPI surface.

JSON mode

sona chat --json

This is the same Gateway-to-channel path, but the wire format is newline-delimited JSON on both directions. Useful for piping into Sona from another program, recording golden transcripts in tests, or driving the daemon 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 (TNS-458):

{"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

CLI sessions are scoped to your local $USER — every sona chat invocation reuses the same (channelId=cli, senderId=<your-username>) pair and thus the same chat history. Killing and re-running sona chat resumes the same conversation.

If you want a fresh conversation, today the cleanest way is:

rm ~/.sona/sessions/cli:<your-username>.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)
StreamingYes (sends one envelope per N tokens)Yes (REPL) / Yes (JSON, per-chunk records)
MediaPhotos, documents, voice (inbound)No
ThreadsYes (per-chat threading)One conversation per $USER
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.

On this page