DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

hanlinlibham /

hanlinlibham/dsh-native-tui

Verified

Classic, dependable DeepSeek Harness TUI with session, provider, and model management.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@5dec6eb6

dsh-native-tui

经典、稳健的 DeepSeek Harness TUI。

它把会话、供应商和模型管理放进终端,保留键盘操作与原生滚动,布局保持紧凑。这里的 “经典”指传统终端式交互;“稳健”来自锁定的 rc.6 依赖和持久会话重放,所有交互都走 官方 Harness service / event / presenter 合同。

dsh-native-tui 的供应商与模型管理界面

这是社区项目,不是 DeepSeek 官方发行版,也不代表 DeepSeek。启动字标沿用上游 DeepSeek Harness TUI 曾实际发布的官方终端字标:DEEPSEEK 使用官方蓝色渐变, HARNESS 使用终端前景色。实现依据是上游提交 d04ec5a, 上游与本项目均采用 MIT 许可证。

项目包名是 @ablemind/dsh-plugin-tui,Cordis 插件入口名是 tui-runner。它作为普通 函数插件挂在 dsh-base 上,接管当前终端,不改 DeepSeek Harness 内核。rc.6 自带的 dsh-headless 明确没有 interactive follow-up surface;这个插件补上交互终端界面。

核心能力

  • 会话管理:新建、继续最近会话、按列表恢复、按 ID 恢复、重命名;历史与实时消息走同一套事件渲染。
  • 供应商管理:/provider 查看 active / dormant 状态、凭据来源和模型目录,可遮罩录入 API Key,并响应官方配置刷新事件。
  • 模型管理:/model 按供应商分组选择模型,接着选择 reasoning effort;结果写回默认设置,下一轮生效。
  • 经典终端交互:键盘优先、低装饰、原生 scrollback;全屏与内联模式可切换,支持 dark / light / mono。
  • Harness 原生能力:工具 presenter、Code Mode、Workflow、子代理、Jobs、HITL 与上下文压缩都从官方服务和事件读取。

设计语言整体沿用 ablework/dpagt/dpcli 的 Python TUI(Textual 版),数据来源换成 harness 自己已经拥有的三样东西。

与 Python 版的关系

照搬的(这些是已经验证过的产品判断,没有理由重做):

  • 无图标、无消息边框;用户消息与工具行靠背景色块区分,assistant 裸 Markdown
  • 工具渐进披露:运行时只露最近 3 条语义行,轮次结束折成 工具 × N;失败与 中止行在折叠后仍然可见
  • braille spinner(⠋⠙⠹… @80ms)+ 当前工具名 + 已耗时 + (Esc 打断)
  • 输入框只有上下两条全宽 ─,无左右竖边;> 提示符常显;! 开头进本机 shell 态(品牌红)
  • Claude Code 形状的三段底栏:左 = 模型 + 上下文占用条(>70% 黄 / ≥85% 红), 中 = 目录短形式 + git 分支,右 = 权限模式 + token 计量;宽屏一行、窄屏两行, 降级顺序固定,模型与上下文占用永不下车
  • 三套配色 dark / light / mono,色值逐条搬过来,/theme 持久化

换掉的(Python 版当年只能自己猜,这里 harness 已经给了权威来源):

数据 Python 版 这里
工具叫什么、动了哪个文件、diff 是什么 名字表 + 参数键启发式 ToolDefinition.presentCall / presentResult 声明的 ToolCallView / ToolResultView;启发式只作为未声明 presenter 的工具(主要是 MCP)的兜底
上下文占用、token 明细 自己从流帧累加 _last_usage(其注释记着一次压缩后仪表卡死的 bug) tokenUsage / contextPressure / contextBreakdown 三个会话投影,从持久日志折出来
transcript 从哪来 自建 SSE 帧 ctx.on('session/event'),用户输入也从事件回显而不是从按键回显 —— 重放渲染与实时一致

结构上唯一的实质差异:Python 版是 Textual 全屏应用,可以事后改任意 widget, 代价是丢掉终端自身的选中/滚动(它的 docstring 里列了三条并行的复制通道来补)。 这里反过来:transcript 就是普通 stdout 滚动区,选中、滚轮、复制全是终端原生的; 只有会变的东西(运行中的工具组、流式 assistant 尾巴、状态行、底栏、输入框) 待在底部固定区,轮次结束时把最终形态打一次进滚动区。因此 “展开某个历史工具行” 不是原地改写,而是 /open 重新打印一遍。

布局

dsh-native-tui/
├── package.json      @ablemind/dsh-plugin-tui — dsh.bundle.patch + rc.6 依赖
├── cordis.patch.yml  组合包 patch:直接叠在 dsh-base 上,与 dsh-headless / dsh-web-app 平级
├── tsdown.config.ts  src/index.ts → lib/index.js(ESM,@deepseek-ai/* 全部 external)
└── src/
    ├── ansi.ts       Span/Line 样式模型、东亚宽字符测宽、截断/填充/换行、SGR 渲染(顶替 rich.Text)
    ├── theme.ts      三套配色 + 解析 + 持久化(顶替 tui_theme.py + palette.py)
    ├── format.ts     preview / 脱敏 / token 数字格式 / 路径收缩(对应 tui/format.py)
    ├── markdown.ts   极简 Markdown → 样式行(顶替 rich.Markdown + pygments)
    ├── screen.ts     滚动区追加 + 底部固定区重绘(核心渲染器,Textual 的替代品)
    ├── composer.ts   raw mode 行编辑、历史、`/` 与 `@` 补全、`!` 态
    ├── tools.ts      ToolRow 语义行 + 渐进披露 + diff;ToolGroup 三行活动窗(对应 tui/tools.py)
    ├── events.ts     可回放的 workflow / compaction / context / inbox 投影
    ├── subagents.ts  直接消费 ctx.subagents 官方目录与会话投影
    ├── statusbar.ts  spinner / 提示行 / 三段底栏 / `/context` 明细(对应 tui/statusbar.py)
    └── index.ts      Cordis 插件:建 agent、分发 session 事件、命令、按键

安装

需要 Node.js 22+。克隆仓库后运行安装器:

git clone https://github.com/hanlinlibham/dsh-native-tui.git
cd dsh-native-tui
./install.sh
$EDITOR ~/.config/ablemind/config.env
dsh .

安装器先执行完整检查,再按仓库锁文件安装 @deepseek-ai/dsh@0.1.0-rc.6 和插件,位置是 ~/.local/share/ablemind,创建 abletui profile,再把启动器放到 ~/.local/bin/dsh。重复运行就是升级;已有配置不会被覆盖。配置文件至少填写 DEEPSEEK_API_KEY,也可固定 model / effort、权限、Code Mode、主题和全屏模式。

如果机器已有别的 ~/.local/bin/dsh,安装器会停止,不会覆盖。若 PATH 中另一个 dsh 排在前面,安装器也会提示你把 ~/.local/bin 提到前面。

日常入口和其他 coding agent 一样:

dsh .                          # 新会话
dsh --continue .               # 当前项目最近一次
dsh --resume .                 # 列表选择历史会话
dsh --resume=SESSION_ID .      # 精确恢复

这个 dsh 仍保留官方入口。dsh plugin ...、dsh web ...、dsh --profile NAME ... 会原样交给安装器固定的 DeepSeek Harness CLI;裸 dsh 或传入目录时才启动 TUI。

开发安装

npm install && npm run check                       # typecheck + 测试 + 构建 lib/index.js
dsh plugin --profile abletui add "$PWD"            # 首次会自动建 profile

profile manifest 落成这样,和 headless / web 同构:

"dsh": { "profile": { "bundles": [
  "@deepseek-ai/dsh-base", "@ablemind/dsh-plugin-tui" ] } }

显式指定 profile

cd 任意项目目录
dsh --profile abletui

没有启动器、没有 --patch、没有 cwd overlay:process.cwd() 就是 agent 的工作目录。

这一段的历史:2026-08-16 之前这个包是 checkout 里的 .ts 源码插件,靠 lab-plugins/ 拷贝 + tsx 源码启动加载,于是进程只能从 checkout 里跑(先 cd 到 项目再起会在 import 阶段炸 does not provide an export named 'FiberState'), 工作目录只能靠一张生成的 overlay 传,还得配一个 tui.sh 启动器。四个补丁同一个 根因。改成"编译产物 + dsh.bundle + npm 依赖"之后一次性全消失—— @deepseek-ai/dsh-*@0.1.0-rc.6 已经发到 npm(rc.5 时代挡路的 @deepseek-ai/dsh-type-meta 在 rc.6 已经不是依赖了),全量 typecheck 对着发布 接口零报错。

要连 dsh 本体也不依赖 checkout:npm i -g @deepseek-ai/dsh@0.1.0-rc.6。 用源码 checkout 起也行(cd dsh && node --import tsx/esm apps/cli/src/bin.ts --profile abletui),只是那样进程 cwd 又被钉回 checkout。

DEEPSEEK_API_KEY 由 app-boot 从进程 cwd 的 .env 读,所以要么放进项目根的 .env,要么直接 export 到环境里。

配置项(cordis.patch.yml 的 config:)

键 含义
fullscreen 占满终端(alt screen,自带 PgUp/PgDn 滚动,退出时把 transcript 回放到普通屏)。默认开;DSH_TUI_FULLSCREEN=0 退回内联模式(滚动与拖选交给终端)
cwd agent 的工作目录(会话 meta.cwd、底栏路径与 git chip、! shell、@ 补全根)。通常不用填——默认就是进程 cwd;只在宿主把进程钉死在别处时才需要(比如源码 checkout 启动)
theme dark / light / mono;不填则用 ~/.dsh/tui-settings.json 里 /theme 存的
contextLength 适配器还没报出容量前,底栏占用条用的窗口大小
settingsFile 配色持久化路径
bangTimeout ! 本机命令的超时秒数(默认 30)
sessionMode new / continue / resume;启动器自动设置
resumeSession 要精确恢复的 session id;启动器自动设置
historyFile 输入历史 JSONL,默认 $DSH_HOME/tui-history.jsonl(未设时 ~/.dsh/...)
provider / model / reasoningEffort 可选的启动模型路由;provider/model 必须一起配置

按键与命令

按键 行为
Enter 发送;补全打开时先接受候选
Alt+Enter 或行尾 \ 换行(raw mode 读不到 Shift+Enter,这里不假装能)
↑ ↓ 历史;补全打开时是选候选
Ctrl+R 按当前输入倒序搜索持久历史
Shift+Tab 打开权限预设弹窗
Tab 接受候选
Esc 关补全;没有补全时打断当前轮
PgUp / PgDn 全屏模式下翻阅历史(内联模式交给终端自己滚)
Ctrl+O transcript(所有工具行常开第二层)
Ctrl+L 清屏
Ctrl+C 运行中→打断;有输入→清空;空输入连按两次→退出
Ctrl+D 空输入时退出
Ctrl+A/E/U/K/W 行首 / 行尾 / 删到行首 / 删到行尾 / 删词

命令:/help /new /clear /resume /rename /context /agents /jobs /todos /tools /open /verbose /theme /model /permission /provider /mcp /quit, 外加未列入补全的 /dump(打印最近一条工具行的完整脱敏 args/result)。 挂上 @ablemind/dsh-plugin-peers 之后还有 /peers(同工作区其它对话)和 /quad(开或复用 tmux 四席)。

/context 是 “底部 token 细节” 的展开版:system / tools / messages 三项构成 (启发式估算),加上末次请求、下次预计、窗口容量、累计输入/缓存读/缓存写/输出。 构成三项与占用不相加——前者是固定密度估算,后者锚定提供方实报,这个差是 token-meter 有意保留的。 非用户的上下文注入与官方 Web UI 一样默认只显示一行来源;/context show 展开最近一条,/context show #<seq> 展开指定编号。

Harness 能力在终端上的消费

  • Code Mode:rootCallId / parentCallId 折成真正的递归工具树;根调用才计入 工具 × N,每层子调用继续走同一套 presentCall / presentResult。
  • Workflow:按 runId 折叠 run/member 生命周期,运行中在固定区更新,结束后进入 scrollback;历史尾先到时会等待更早的 run-start。
  • 子代理:不把 child transcript 灌入主对话。/agents 读官方子代理目录,显示 one-shot/continuable、存活、token、活跃耗时和后代。
  • 压缩:关联 start/summary/replacement/end,显示 summary、被遮蔽项/token、路由和耗时; compaction/prune 保持官方 log-only 计量语义。
  • 上下文注入:非用户 user/message 按 durable source 分为 inject/recall,指令文件、 skill、plugin 与 session-reference 都不再静默丢弃;对齐官方 Web UI 默认折叠,需要时再用 /context show [#seq] 显式展开。
  • HITL:approval 绑定 callId;提问支持完整 batch、skip、多选+自定义、精确 plan-review 识别,并区分用户取消与 harness abort。
  • Jobs / 产出:TUI 挂上官方 jobs controller,/jobs output|kill <id> 直接走 registry 的 read/kill 合同;每轮成功 mutation 按 tool render intent 折出去重的产出文件,不靠工具名猜测。

Known Limitations and Deferred Work

  • 跨会话全文检索尚未接入。/resume 能选择并重放已有会话,但没有 /history 或基于 sessionQuery 的跨会话搜索;中文 FTS 分词与字面扫描降级也因此仍是延期工作。
  • 没有通用插件、Skill 与设置清单。/mcp 只按当前注册工具分组,尚无 /plugins Loader inventory、/skills registry 列表或通用只读 /settings;/provider 只管理供应商相关配置。
  • 没有会话导出。/copy 只用 OSC 52 复制最近一条助手回答,尚无 /export JSONL/Markdown。
  • spill notice 仍按普通工具文本显示。完整结果路径仍在文本里,但尚未折成“已溢出 N 字节 → path”的专用定位行。
  • 固定区假设自己是唯一写 stdout 的人。同 profile 里别的插件(或 logger)直接 往 stdout 写,会把底部区顶上去一次,下一次重绘才恢复。真要共存,得给 logger 换一个走 screen.printRaw 的 sink。
  • 历史工具行不可原地展开。滚动区是追加式的,/open 是重新打印而不是改写;/dump 查看最近一条完整脱敏载荷。
  • /model 弹窗切换。借鉴 AbleMind Code v0.2.0:←→ 切 provider 分组,↑↓ 选择模型;支持模型后的第二级 reasoning effort,下一轮请求生效并写回默认选择。
  • /provider 供应商管理。借鉴 AbleMind Code v0.2.0 的双栏弹窗:↑↓ 选择供应商,右侧查看 active/dormant、配置、凭据来源和模型目录;k 遮罩录入 Key,首次配置会像官方 Web 一样先用 path mutation 建立 credential reference,再把秘密只写入 ctx.credentials。页面状态按 provider directory × redacted settings × credential describe 合并,并随三类官方 invalidation 自动刷新;r 也可手动刷新。
  • MCP 是配置期能力。rc.6 没有运行时 manager service,/mcp 如实列出当前已注册的 mcp__* 工具;增减服务后需重启,TUI 不伪造 toggle/reload。
  • 图片与 Mermaid:会话图片按官方 attachment 元数据列出,/file 可预览文本与识别图片; 跨终端没有统一的图形协议,因此不把 Kitty/iTerm 私有协议冒充通用渲染。Mermaid 源码按 Markdown code fence 展示,浏览器图形画布仍属于 Web 插件。
  • 多字节候选宽度:补全列表按候选条数截窗,不按像素宽度,超宽候选会被行截断。
  • @ 补全只补路径文本,不挂附件。补出来的 @path 就是发给模型的普通文本 (模型可以自己去读),没有走 dsh-attachment 的内容寻址存储。web 面的 ui-input-trigger 那套内联引用语义这里还没有对应物。
  • 渲染节流 33ms。流式 chunk 实测超过 100/s,每帧全量重绘固定区会撕裂;按键走 leading edge 所以输入仍是即时的,但极端情况下最后一帧可能滞后一帧。
—/ 5

No ratings yet

Verified DSH bundle

Commit 5dec6eb6ead3

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout