dsh-tui

August 15, 2026 · View on GitHub

English  ·  简体中文

release CI downloads Go license

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.

  1. Install the host: follow deepseek-harness installation instructions (npm global package @deepseek-ai/dsh).

  2. Configure an LLM API key for the host (see deepseek-harness docs).

  3. 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

KeyAction
Ctrl+CQuit
Ctrl+E / EndJump to bottom
PgUp / PgDnPage up/down
↑/↓ / mouse wheelScroll (text selection: Shift+Click, terminal standard)
EscInterrupt current turn (session.cancel)

Input box

KeyAction
EnterSend (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
PasteBracketed paste (Cmd/Ctrl+Shift+V)
exit + EnterQuit the program

Paragraph focus mode

KeyAction
Tab / Shift+TabFocus next/previous expandable paragraph
↑/↓Move between paragraphs in focus mode
EnterExpand/collapse (bash output, edit diff, grep groups, thought text)
EscBack to input

Overlays

KeyAction
(idle, empty input)Session list: ↑↓ navigate · Enter switch · Esc close
Ctrl+GTheme: ↑↓ pick auto/dark/light/colorblind · Enter apply · Esc close (auto re-queries terminal background; 5s polling)
Ctrl+MModel: ↑↓ pick · Enter select (with default effort) · E effort panel · Esc

Approval / question overlays

KeyAction
↑↓ / j / kMove cursor
EnterConfirm (approval: allow once / deny; question: option / Other custom answer)
EscDeny / 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 error cancelled)
  • Trust: loopback Host header passes
  • Event source: append-only SessionEvent log (user/message, assistant/chunk, tool/call+result, todo/write, turn/end, etc.; plugin-extensible)
  • Reconnect: downlink auto-rebuild + session/subscribed seq comparison to backfill history gaps
  • Projections: session/projection frames + 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