Metadata-Version: 2.4
Name: zeno-channel-cli
Version: 1.1.0
Summary: Terminal channel for Zeno — stdin/stdout with streaming.
Project-URL: Homepage, https://github.com/nkootstra/zeno
Project-URL: Repository, https://github.com/nkootstra/zeno
Project-URL: Issues, https://github.com/nkootstra/zeno/issues
Project-URL: Changelog, https://github.com/nkootstra/zeno/blob/main/CHANGELOG.md
Author: Niels Kootstra
License-Expression: MIT
License-File: LICENSE
Keywords: agent,ai,channel,cli,terminal,zeno
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: zeno-core
Description-Content-Type: text/markdown

# zeno-channel-cli

Terminal channel for [Zeno](https://github.com/nkootstra/zeno). Reads
lines from stdin into `IncomingMessage`s and renders the agent's
`OutgoingMessage`s to stdout. Doubles as a manual smoke-test harness
and as the simplest reference implementation of the `Channel` protocol.

## Install

```bash
uv add 'zeno-framework[cli]'
# or, without the AI package:
uv add zeno-channel-cli
```

## Minimal usage

```python
import asyncio

from zeno.agent import Agent
from zeno.app import ZenoApp
from zeno.channels.cli import CliChannel
from zeno.testing import FakeProvider

app = ZenoApp(
    agent=Agent(name="root", instructions="You are Zeno, a helpful assistant."),
    channels=[CliChannel()],
    provider=FakeProvider(),  # swap for OpenAIProvider / ClaudeSDKProvider in production
)

asyncio.run(app.run())
```

Then run the script. Type a line, hit return, see the reply. `Ctrl-D`
or `Ctrl-C` exits cleanly. `user_id` defaults to `$USER` (or `"local"`
if unset) so a developer running the snippet above gets a sensible
tenant scope without configuration. Override explicitly in production:

```python
CliChannel(user_id="alice", prompt=">>> ")
```

## Behavior

- **Capabilities**: `supports_streaming=False`,
  `supports_threading=False`. The core `StreamBuffer` consolidates
  chunked replies into one `send()` at finalize-time, which the
  renderer emits as a single line.
- **Stdin**: read via `asyncio.to_thread(stream.readline)` so the CLI
  channel stays dependency-free (no `aioconsole`). At most one line
  read is in flight at a time, matching REPL semantics.
- **Inbound queue**: exposes `inbound_put(message)` so
  [`zeno-scheduler`](https://github.com/nkootstra/zeno/tree/main/packages/zeno-scheduler)
  can inject proactive messages without going through stdin.
- **Shutdown**: `stop()` is idempotent and closes the renderer; calling
  `send()` after `stop()` raises `ChannelError`.

## Testing

```bash
uv run pytest packages/zeno-channel-cli
```

Tests use `io.StringIO` for stdin/stdout — no terminal needed.

## See also

- [`zeno-channel-email`](../zeno-channel-email/README.md), [`zeno-channel-sendblue`](../zeno-channel-sendblue/README.md) — other first-party channels.
- [`docs/adapters.md`](../../docs/adapters.md) — writing your own `Channel`.

Part of the [Zeno framework](https://github.com/nkootstra/zeno).
