dsh-tui
August 15, 2026 · View on GitHub
English · 简体中文
dsh-tui
A Go terminal client for deepseek-harness. The rendering layer is ported from waveloom's Bubble Tea v2 TUI.
Pure client architecture: dsh-tui connects to a dsh host process over HTTP/WS,
fully decoupled from the host — the host runs dsh web as a resident, and the web
UI plus any number of dsh-tui windows share the same instance.
Install
One-command install script
macOS / Linux(installs to ~/.local/bin):
curl -fsSL https://raw.githubusercontent.com/Menfre01/dsh-tui/main/install.sh | sh
Windows(PowerShell):
powershell -ExecutionPolicy Bypass -Command "iex (iwr -UseBasicParsing https://raw.githubusercontent.com/Menfre01/dsh-tui/main/install.ps1)"
The script detects OS/architecture, downloads the matching package from GitHub Releases, verifies SHA256 (Windows), and handles PATH.
Homebrew
brew install menfre01/tap/dsh-tui
Manual install
Download dsh-tui_<os>_<arch>.tar.gz (unix) / .zip (windows) from
Releases, extract, and put the
binary on your PATH.
Build from source
make build # current platform → bin/dsh-tui
make release # cross-compile 3 platforms × 2 arches → dist/ (with checksums.txt)
Only needed for development; the install scripts above give you a ready binary.
Before you start
dsh-tui is a pure client — it needs a running dsh host to talk to.
-
Install the host: follow deepseek-harness installation instructions (npm global package
@deepseek-ai/dsh). -
Configure an LLM API key for the host (see deepseek-harness docs).
-
Start the host (resident; the web UI and every dsh-tui window share it):
dsh web # default: http://127.0.0.1:3080
Then use dsh-tui as a normal command — no Go toolchain needed for the installed binary.
Quick start
# TUI client (any directory, multiple instances) — connects to 127.0.0.1:3080 by default
dsh-tui # new session
dsh-tui --url http://192.168.x.x:3080 # connect to a remote host
dsh-tui --locale zh-CN # force locale (auto follows host locale.preference)
Once inside:
- Type a message and press Enter to send.
- Press
←while idle to open the session list — switch to an existing session with Enter, or Esc to close. No need to know session ids. --resume <id>is optional: it restores a specific session at startup; the same sessions are reachable via the←list.
Key bindings
Global
| Key | Action |
|---|---|
| Ctrl+C | Quit |
| Ctrl+E / End | Jump to bottom |
| PgUp / PgDn | Page up/down |
| ↑/↓ / mouse wheel | Scroll (text selection: Shift+Click, terminal standard) |
| Esc | Interrupt current turn (session.cancel) |
Input box
| Key | Action |
|---|---|
| Enter | Send (queued when busy); Ctrl+Enter toggles mode (queue↔steer); on empty input with an update banner: install the new version |
| ↑/↓ | Input history navigation (when idle) |
| Esc Esc (within 500ms) | Clear the input box |
| Paste | Bracketed paste (Cmd/Ctrl+Shift+V) |
exit + Enter | Quit the program |
Paragraph focus mode
| Key | Action |
|---|---|
| Tab / Shift+Tab | Focus next/previous expandable paragraph |
| ↑/↓ | Move between paragraphs in focus mode |
| Enter | Expand/collapse (bash output, edit diff, grep groups, thought text) |
| Esc | Back to input |
Overlays
| Key | Action |
|---|---|
← (idle, empty input) | Session list: ↑↓ navigate · Enter switch · Esc close |
| Ctrl+G | Theme: ↑↓ pick auto/dark/light/colorblind · Enter apply · Esc close (auto re-queries terminal background; 5s polling) |
| Ctrl+M | Model: ↑↓ pick · Enter select (with default effort) · E effort panel · Esc |
Approval / question overlays
| Key | Action |
|---|---|
| ↑↓ / j / k | Move cursor |
| Enter | Confirm (approval: allow once / deny; question: option / Other custom answer) |
| Esc | Deny / cancel (in Other input mode: back to options) |
Architecture
cmd/dsh-tui/ entry (connection/subscription/callback wiring)
internal/dsh/ wire protocol client (quadrant RPC + dual WS downlink + respond)
internal/tui/ rendering layer (waveloom port: paragraphs/theme/HUD/overlay) + event projection
wire protocol (confirmed against upstream source)
- Unary:
POST /api/<method>,{type:"client-request", rpcId, method, payload} - Downlink: two receive-only WebSockets (
/api/events.mux+/api/events.host), frames =server-request - Respond: approval/question frames →
POST /api/respond(rpcId echoed; question cancel via RPC errorcancelled) - Trust: loopback Host header passes
- Event source: append-only
SessionEventlog (user/message, assistant/chunk, tool/call+result, todo/write, turn/end, etc.; plugin-extensible) - Reconnect: downlink auto-rebuild +
session/subscribedseq comparison to backfill history gaps - Projections:
session/projectionframes + session.list projections (host-authoritative contextPressure/sessionStats/tokenUsage/title)
Host alignment
- Tool rendering: bash/read/write/edit(diff)/grep/glob(search)/web_fetch/web_search/skill/ask_user_question/todo_write/job_output/list/kill/read_image/pwsh/ralph/goal/subagent controls — summary-line args + structured suffix (exit code/match count/path count/status)
- HUD: ctx progress (projectedTokens first, exact percentage, pressure coloring), turns/elap/tok/cache from host projections, effort shown as
(effort ...) - Config following: busyEnter (queue/steer, Ctrl+Enter toggles), locale.preference (language), auto theme (terminal background detection + polling)
- Multi-session: mux frames filtered by sessionId; send target synced on switch/new — no cross-talk
- Session list: visibility rules aligned with dsh web (blank/subagent filtering), host title projection, fixed-height window
Development
make build # build (env vars point to workspace caches)
make test # unit tests (dsh wire + tui interaction/layout/projection)
make vet
make lint # golangci-lint (waveloom-aligned linter set)
make dump # headless render verification (no TTY)
Protocol-drift protection: the wire layer is isolated in internal/dsh; the dsh
version is visible via host.describe.version. Known compatible: dsh master @ 2026-08-14 (0.0.1).
Status
- Wire layer (spike-verified against real dsh)
- Rendering layer port (full waveloom visuals: logo/header/HUD/theme)
- Approval (↑↓ select) / question (↑↓ + Other custom) respond loop
- Session switch/new, theme, model selection (with effort panel)
- Reconnect event backfill
- Pure client architecture (shared dsh web host)
- Tool view alignment (diff/read/search structured + all-tool summaries)
- HUD vs host projections (ctx/turns/elap/tok/cache/effort/title)
- fork/rename/search, agentPreset, subagent views