Back to home

omdsh-plugins

omdsh-tui

Workspace root for the DeepSeek Harness interactive terminal: the TUI surface, its profile bundle, and the vendored terminal UI library.

Stars
0
Language
TypeScript
Created
Aug 16, 2026
Updated
Aug 16, 2026

Introduction

omdsh-tui

English | 中文

An interactive terminal for the DeepSeek Harness, as an installable profile bundle.

Upstream removed its TUI package. This repository carries one, built against a published harness release rather than a fork of it — which is what lets the terminal follow harness releases by changing a version number instead of merging a monorepo.

Packages

PackageWhat it is
packages/tui@omdsh-plugins/omdsh-tui — the terminal surface: transcript rendering, the editor, autocomplete, tool cards, status rows
packages/tui-app@omdsh-plugins/omdsh-tui-app — the profile bundle: cordis.patch.yml, the command line, session identity, and the /resume process handoff
vendor/tui@deepseek-ai/tui — source-vendored terminal UI library; see its README for provenance and local modifications

Modes

The terminal composes each session from one agent preset — the plugin composition deciding which tools the model has, what its persona says, and which prompt sections it sees — and /mode is the surface over the four dsh ships:

ModePreset idWhat the model gets
Standard modestandardThe full coding agent: file editing, shell, file and web search, skills, planning, goals, subagents, workflows
Code modecodeStandard's capabilities presented through the Code Mode SDK, so a five-call sequence becomes one TypeScript program
Minimal modeminimalPersistent bash and str_replace_editor, a one-sentence persona, and nothing else
Creator modecordisStandard plus the self-referential Cordis toolset and a preset-authoring skill, for asking an agent to write you another agent

/mode opens a picker; /mode <id> selects one directly. A session that has already said something may still switch — the harness calls the blank-session limit "a product rule, not a mechanical one", and in a terminal the person typing is the session's owner, so this surface takes that judgement and states the one real consequence: which earlier tool calls the new mode cannot repeat. Only a running turn holds it, because the catalog has to stay steady under the step in flight. /mode default <id> separately names the mode new sessions start in, writing the same agent-presets user setting the Web surface's General settings row writes. A committed switch is logged, so /resume rebuilds a session under the composition its history was produced under.

The bundle patch is what makes this a real choice rather than a label: it disables the base's host-plane tool rows so a preset that removes a tool actually removes it. See the bundle's README for where that line is drawn, and the front door's for what /mode does.

The harness release it builds against

pnpm-workspace.yaml's catalog pins one release for every harness package at once. The dsh-* entries ride the release line this terminal is developed against; cordis and schemastery take the stable versions that release itself depends on. Following a new harness release is moving the catalog — the package versions follow it — and running the tests.

pnpm run check:harness-pin proves two things: that the catalog names exactly ONE release — a catalog that pinned two would compose a runtime out of halves that never shipped together, and pnpm would install it without complaint — and that every package in packages/ carries that release as its own version. A package version here is not the terminal's own bookkeeping: it names the DeepSeek Harness release the terminal builds against, so 0.1.0-rc.7 while the catalog pins 0.1.0-rc.6 would claim a release that does not exist, and the check fails on it. The versions move only with the catalog.

The startup banner reads @deepseek-ai/dsh-agent's manifest — a peer dependency, so at runtime it is the harness the profile actually composed — and prints the result as DeepSeek Harness v<version>: the same claim the version fields make, read from the harness itself rather than from a manifest this repository publishes.

Install

A global dsh on PATH — every step below shells out to it — plus Node ^22.19.0 || >=24.0.0 and pnpm 11.7.0, as engines and packageManager state. Interactive terminals on macOS, Linux, and Windows are all supported; Windows goes through pi-tui's native console VT-input handling.

A bundle is an npm package declaring "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }, which a profile applies as a patch layer over dsh-base. Nothing here is published, so a profile installs it from a checkout:

pnpm install
pnpm run install:profile     # build, install into the `omdsh-tui` profile, verify it composes
dsh --profile omdsh-tui

That script is the manual steps plus the checks that they worked:

pnpm run build
dsh plugin --profile omdsh-tui add "$PWD/packages/tui" "$PWD/packages/tui-app"
dsh --profile omdsh-tui --dump-config    # the composed layer stack, without booting

Both packages, not just the bundle. The Loader resolves a row's module specifier from the profile directory, and the bundle patch names @omdsh-plugins/omdsh-tui in two rows. A published bundle would pull the front door in as an ordinary dependency; a checkout cannot, on two counts — pnpm does not install a link: dependency's own dependencies, and the bundle names it through workspace:^, which means nothing outside this workspace. Installing it prints declares no dsh.bundle — installed as a plain dependency: that is the correct outcome for a library a bundle imports, not a problem.

A composed config is not proof of a bootable one: --dump-config reads patch files and never imports a module, so a tree that dumps perfectly still dies at boot with ERR_MODULE_NOT_FOUND if a row names a package the profile cannot resolve. install:profile checks both.

Install under any profile name you like (pnpm run install:profile -- --profile <name>); nothing in the terminal is bound to one, because the exit line, the help text, and the in-place /resume handoff all read the name this process was booted under out of argv.

omdsh-code needs this profile. Its column is a terminal running dsh --profile omdsh-tui, so a Web profile carrying Code mode without this one installed renders dsh: profile "omdsh-tui" does not exist inside the column and nothing else. The two are separate installs into separate profiles — the terminal never goes into the Web profile itself, for the reason under ## Known limitations.

Remove it by naming the two packages the install added:

dsh plugin --profile omdsh-tui remove @omdsh-plugins/omdsh-tui-app @omdsh-plugins/omdsh-tui

What is left is a dsh-base profile with no surface, which is not something to boot; rm -rf ~/.dsh/profiles/omdsh-tui takes the profile itself.

Upgrading from a harness release that shipped a terminal

The name omdsh-tui is deliberate. tui belonged to the harness installation itself while a TUI shipped in the box, and ~/.dsh does not migrate when dsh is upgraded — so a machine that ever ran one still has a ~/.dsh/profiles/tui naming a bundle the current dsh cannot resolve:

Error: dsh: cannot resolve profile bundle "@deepseek-ai/dsh-tui-app" from the dsh installation or ~/.dsh/profiles/tui; run 'dsh plugin --profile tui install' if its dependencies are not installed.

dsh plugin add does not clear that entry. It reconciles only the bundles it manages as dependencies, which is exactly what keeps @deepseek-ai/dsh-base safe from removal — but it also means a stale entry is never re-checked, so add succeeds and the next boot is what fails. The error's own advice cannot help either: the package exists nowhere to install from.

Installing under omdsh-tui avoids the collision. To keep using the tui name, point the installer at it — it drops any bundle that no longer resolves, and prints what it dropped:

pnpm run install:profile -- --profile tui

Commands

pnpm install
pnpm run build             # tsc emits lib/types per project, tsdown bundles each member
pnpm run install:profile   # build, then install the bundle into a dsh profile and verify it
pnpm run check:harness-pin # the catalog names one release, and every package carries it
pnpm run typecheck         # sources, tests, and scripts; the vendored library keeps its own relaxed flags
pnpm run test              # unit tests
pnpm run test:snapshot     # terminal-frame replays against the recorded output (DSH_SNAPSHOT=refresh re-records)
pnpm run clean             # remove every member's lib/, the vendored one included, and the tsbuildinfo files

install:profile takes --profile <name> and --skip-build after a --. The vendored library is upstream source held to relaxed compiler settings, so the checks project reads its emitted declarations rather than re-checking it under this repository's flags.

Where this came from

Split out of a fork of the harness monorepo, where it was packages/tui/tui, packages/bundle/tui-app, and vendor/tui. Design rationale is in docs/: the TUI profile front door, and the bottom-anchored surface. docs/tui.md (中文) is the reader's document rather than a decision record — what the surface is, what ctx.tui offers a plugin that wants the screen, and what a launcher controls.

One function came along that upstream does not publish: resolveHarnessCheckout, the walk to a harness checkout root that the harness-source prompt section needs to be honest about. It lives in packages/tui-app/src/checkout.ts and can be dropped if a harness release ever exports it.

Known limitations

  • A profile composes exactly one surface, and this is one. This bundle's patch and dsh-web-app's target the same base rows — system-prompt, tools, and two dozen more — so a profile carrying both does not get two front doors; it gets whichever layer landed last, row by row. It belongs in a profile of its own, which is why it is absent from the collection's registry and why install:profile builds a profile rather than adding to web. A TUI profile is built, not added to a running Web one.
  • It needs a real terminal. Both stdin and stdout must be TTYs. Scripts and Loader pipes want the one-shot @deepseek-ai/dsh-headless app instead; this one has nothing to draw on.
  • Nothing here is published. Every install is a checkout install, which means dsh plugin records a link: and the installed files ARE this working tree — rebuild after editing, and expect the plugin hub to report linked rather than a version.
  • There is no harness:local / harness:npm switch. Unlike the plugin repositories, this one has no scripted way to build against a harness checkout: following unreleased harness work means editing the catalog's dsh-* lines by hand and putting them back afterwards.
  • The vendored library is a fork you own. vendor/tui carries modifications upstream does not implement, so a new upstream pi-tui is a merge here rather than a version bump.