A Claude-Code-style terminal chat client that connects directly to the control plane, bypassing Discord, and mounts the operator's working directory so nano runs tools directly against their files — no upload, no catbox, no abstraction step.
what's here #
Server side (niri-runtime)
SwitchableToolClient— delegates to the default (local) tool client but can be re-pointed at a remote executor at runtime; reverts on disconnect. One failing/unreachable client can't wedge the default.POST /client/connect{ endpoint, workspace?, capabilities? }— mounts a connecting client's executor as the active tool target (does an executor health handshake, 502 if unreachable).POST /client/disconnect— revert to the default client.
Client side (apps/tui, new @niri/tui package)
- Boots an in-process tool executor bound to
$PWD. - Performs the
/client/connecthandshake, then streams/chat/stream(SSE) with ANSI rendering of text / thinking / tool-call / usage events, sends via/trigger/chat, and auto-disconnects on Ctrl-C //quit. - Supports remote control planes via
--advertise+--listen-hostfor the eventual homura split. - Zero new heavy dependencies — built on node stdlib (readline + ANSI) + existing workspace packages.
how it fits the "control plane / client" model #
This is the concrete first step toward decoupling nano's continuity from the reef container. The control plane owns the loop/session; the client is a replaceable thing you run from anywhere that lends nano hands in your $PWD.
verification #
npm run typecheckgreen across all 14 workspaces (incl. new@niri/tui)- unit tests for switch / revert / unreachable paths (
switchable.test.ts) - verified end-to-end: real
apps/tuiexecutor mounted via the handshake, control plane ran a realshelltool against the mounted pwd and got the file contents back
Refinement to follow in review — this is the first-round PR as requested.