dsh-tui
Verified@dopejs/dsh-tui Β· v0.10.0 Β· MIT
A plugin-native terminal user interface for DeepSeek Harness
Install
dsh plugin add @dopejs/dsh-tui Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Creators
Readme
dsh-tui
dsh-tui is a terminal user interface for
DeepSeek Harness. It is
distributed as an out-of-tree Harness bundle and runs in the same process as
the agent runtime.
[!IMPORTANT] This is
0.10.0. Harness peers are declared^0.1.0-rc.6and optional: thedshCLI provides the Harness runtime, so nothing installs them on our behalf and npm must not try. Both the global and local installs are verified against the host's currentlateston every CI run.
Capabilities
The TUI:
- create and resume agents through
ctx.agents; - render the durable
session/eventlog without depending on Web client code; - use tool-owned presentation intents for terminal, diff, search, read, and Web results;
- provide terminal adapters for approvals, user questions, and commands;
- treat every acquired agent handle, listener, prompt, and terminal mode as an explicitly owned resource;
- stay compatible with user profile patches and third-party Harness plugins.
The initial architecture is deliberately same-process. A remote transport can be considered later as a separate adapter and product mode, not mixed into the first implementation.
Prerequisites
The TUI drives a model through Harness, so the profile needs a provider credential before a session can do anything. Without one, a run fails with the provider's own message rather than silently producing nothing:
export DEEPSEEK_API_KEY=... # or store it through the credentials service
Run dsh --profile tui --doctor at any time for a read-only check of services,
model selector, session persistence, and terminal capabilities. It starts no
session and runs no agent.
Install
npm install -g @deepseek-ai/dsh @dopejs/dsh-tui
dtui
dtui initializes the tui profile on first run and starts it. Every argument
is passed through, so dtui --resume <id>, dtui --doctor, and
dtui --print "β¦" all work.
The command is dtui rather than dsh-tui, because
@deepseek-harness-tui/dsh-tui already
claims that name and both should be installable side by side.
When the screen fuses rows together
Moving the terminal cursor onto the caret is what lets an input method compose inside the composer. It also makes Ink take its cursor-only redraw path, whose arithmetic disagrees with the full-redraw path about where the bottom of the output is. Turn the whole mechanism off to get exactly the rendering that came before it:
DSH_TUI_CURSOR=off dtui
If the screen stops corrupting, that mechanism is the cause; say so and it can be fixed rather than guessed at. The caret is still drawn either way β only an input method's composing text moves out of the composer.
When the mouse does nothing
A terminal reporting nothing, reporting something the parser does not know, and reporting something nothing acts on all look the same from inside. Record what the terminal actually sent:
DSH_TUI_INPUT_LOG=/tmp/dsh-tui-input.log dtui
Scroll and click, quit, then read /tmp/dsh-tui-input.log. A wheel notch
appears as \u001b[<64;12;5M; an empty file means the terminal was asked and
answered nothing. Toggle mouse reporting in the command menu hands the mouse
back to the terminal, which restores drag-to-select.
Mouse reporting is a terminal-wide mode: while it is on, the terminal makes no selection anywhere, so clickable regions cannot capture the mouse while the rest of the text stays selectable. Shift-drag selects without turning reporting off.
Install from this checkout
Prerequisites are Node.js ^22.19.0 || >=24.0.0 and pnpm 11.7.0.
pnpm install --frozen-lockfile
pnpm check
npm pack --pack-destination /tmp
pnpm dlx --allow-build=node-pty @deepseek-ai/[email protected] \
plugin --profile tui add \
/tmp/dopejs-dsh-tui-0.10.0.tgz
pnpm dlx --allow-build=node-pty @deepseek-ai/[email protected] --profile tui
Start a persisted session again with:
pnpm dlx --allow-build=node-pty @deepseek-ai/[email protected] \
--profile tui --resume <session-id>
Select an exact provider/model before creating a new session with:
pnpm dlx --allow-build=node-pty @deepseek-ai/[email protected] \
--profile tui --model deepseek-official/deepseek-v4-flash
The route is resolved through the public Harness model registry before any
agent or session is created. --model is intentionally rejected with
--resume; resumed sessions retain their established selection semantics.
The composer supports multiple lines, Unicode cursor movement, selection,
undo/redo, bounded command history, and bracketed paste. Press Enter to submit,
Ctrl-J or Alt-Enter to insert a newline, Ctrl-S to steer, and Ctrl-C to clear or
cancel according to the current state. Use arrows/Home/End to edit, Up/Down at
the document boundary to recall history, and /exit for a graceful, durable
shutdown. Approval prompts accept y or n; structured questions use arrows,
Space, Tab, and Enter.
The transcript follows new output until navigation detaches it. Use Ctrl-Up or Ctrl-Down for one retained entry, PageUp or PageDown for a page, and Ctrl-Home or Ctrl-End for the retained window boundaries. Ctrl-F opens bounded transcript search. Ctrl-T folds the focused tool, Ctrl-Shift-T toggles compact tool cards, and Alt-PageUp or Alt-PageDown pages through a long focused tool result. Ctrl-Shift-C requests an OSC 52 copy of the currently visible transcript when the terminal supports it.
Ctrl-P opens a fuzzy command palette that combines the exact agent's Harness
commands with TUI navigation actions. Selecting an argument-free command runs
it; a command with an input hint is inserted into an empty composer for review.
Tab completes a leading slash-command, an explicit @workspace/path, or a
path-like token containing a directory separator. Completion is bounded to the
current workspace and never discards an existing draft.
The header degrades by terminal width and shows only available facts: exact
model, permission/approval state, cumulative reported tokens and context
capacity, then workspace. Missing upstream facts are omitted rather than
guessed. Default key bindings and the default, high-contrast, and no-color themes
are validated as one atomic preference object. Preferences persist through
ctx.settings when a writable provider exists and are reported as
process-only when it does not. Panels name semantic tones rather than colors,
so dropping color loses no information, and a screen-reader mode removes box
drawing. Reduced motion and key-binding overrides live in the same document.
Ctrl-O opens the bounded session center. Type to filter persisted-session metadata, use arrows to select, Space to inspect durable event metadata, R to refresh, and Enter to switch. Switching is allowed only with an idle agent and empty composer; the old attachment is fully flushed and disposed before the selected session resumes.
Every panel is reachable from the command palette, so a terminal that cannot emit a chord can still get to all of them:
| Panel | Palette action | Shortcut | What it shows |
|---|---|---|---|
| Activity | Open activity | Ctrl-Y | Plan, job, and subagent notifications, coalesced, with navigation into the panel that answers each |
| Jobs | Open jobs | Ctrl-B | Background jobs, ownership, status, and cancellation behind a confirmation |
| Subagents | Open subagents | Ctrl-G | The delegation tree with lineage, depth, follow-up, interrupt, and attach |
| Projections | Open projections | Ctrl-U | Plan, todo, goal, and usage read models |
| Skills | Open skills | β | Discovered skills; Enter inserts /name for you to submit |
| MCP | Open MCP inventory | β | Bridged tools grouped by server |
| Plugins | Open plugins | β | Loader entries, fiber phase, and failure diagnostics |
| Attachments | Open attachments | β | Stage images for the next message |
| Changes | Open changes | β | Durable tool-presented file edits, linked when the terminal supports OSC 8 |
| Recovery | Open recovery | β | Durability barrier, raw export, and conversation fork |
Some capabilities are reported as unavailable rather than approximated, because this Harness baseline publishes no seam for them: job output stays with the owning agent, hooks have no inventory, MCP exposes no connection health, and plugin enablement has no public transaction. Each panel says so in place.
Non-interactive use
--print runs one prompt with no terminal state and exits:
dsh --profile tui --print "summarize this repository"
echo "summarize this repository" | dsh --profile tui --print --output-format stream-json
--output-format selects text (default), json, or stream-json. Envelopes
carry a schema version, arrive in durable event order, and skip events the
contract does not model. stdout carries only run output and stderr only
diagnostics. Exit codes are 0 completed, 1 failed, 2 a run that needed a
human β approvals and questions fail closed, because there is no terminal to
answer on β and 130 cancelled.
Open the command palette and choose Open permissions to inspect every
Harness-provided preset before changing it. Each row shows the resulting
sandbox mode and approval policy. Safe presets apply directly to the exact
attached session; danger-full-access requires typing
enable danger-full-access in full. Escape cancels confirmation without
changing either permission knob. Typing the upstream
/permission danger-full-access command is also treated as explicit selection.
Choose Open changes in the palette to review bounded, file-grouped diff presentations and jump back to the retained durable tool event. Choose Open recovery to run an exact-session durability barrier, export a backend-owned raw artifact without overwriting an existing path, or create an explicitly confirmed conversation-only fork. Harness rc.6 has no public file checkpoint owner, so file rewind is shown as unavailable and presented diffs are never reverse-applied.
Documentation
- Product requirements
- Production product design
- Architecture
- Implementation plan
- Failure recovery matrix
- Testing strategy
- Upstream compatibility
- Architecture decisions
Current baseline
The design was validated against DeepSeek Harness commit
47f943859bef60e4160492346772ded9b24f765a
and the installable 0.1.0-rc.7 package artifacts published from that source
baseline. Harness is still a developer preview, so compatibility is tracked
explicitly rather than assumed.
Development
pnpm check
pnpm build
pnpm bench
The check verifies documentation links, extensionless local TypeScript imports,
lint, TypeScript types, unit tests, fixed-size rendering snapshots, and PTY
lifecycle tests. pnpm test:package additionally builds and installs the actual
tarball into a clean temporary profile, then exercises fresh and resumed PTY
sessions. The benchmark compares the selected Ink adapter with the low-level
Terminal Kit spike; its local timing remains diagnostic rather than a portable
CI budget.
CI runs the blocking check on Linux, macOS, and Windows, and covers both the minimum Node 22 baseline and the current Node 24 line. POSIX process-signal PTY cases run on Linux and macOS; Windows runs the interactive Ctrl-C and supported terminal lifecycle paths.