dsh-whale-tui
grok-build 风格的 DeepSeek Harness 终端 TUI —— 自研完整实现,作为 DSH 插件发布。

鲸鱼在终端里替你干活。
当前状态:P1 全量完成。真实线上事件解析(assistant/chunk、usage、turn/end)、Esc 状态机(取消/清屏/回溯)、审批与 ask_user 双向通道、模型切换、权限预设、会话恢复(/resume)、计划审查(a/s/c/y/q)、任务面板(Ctrl+G)、剪贴板、主题量化、终端探测。demo 模式(--demo)无需 runtime/API key 即可体验全部交互。
设计依据
- docs/01-grok-tui-spec.md —— grok-build pager 交互细节复刻 spec(键盘绑定、Esc 语义、审批弹窗、工具卡、主题槽位等)+ DSH 落点对照 + P0/P1/P2 优先级
- docs/02-openma-teardown.md —— openma/deepseek-harness-tui(唯一 Rust/ratatui 同类)架构拆解 + 差距清单 + 可复用/要重做清单
- docs/04-dsh-capability-map.md —— deepseek-harness 0.1.0-rc.6 能力地图:29 个
ctx.* seam、13 个 session projection、19 个模型侧工具、32 个官方 web surface 对照,以及 DSH 独有能力的 TUI 落点与优先级(goal/GoalBar、上下文压力、沙箱、持久 PTY、cordis 自指插件…)
docs/01 回答「grok 怎么做 TUI」,docs/04 回答「DSH 有什么值得我们暴露」。两者方向不同,不要互相当替代。
架构(与 openma 同构,但协议双向化)
+------ plugin 模式(推荐) ---------------------------------------+
| dsh --profile tui(宿主 Node 进程) |
| |- dsh-base + 本插件 tui-runner 行(cordis 插件, inject agents) |
| | spawn(dsh-tui --attach-fds, stdio inherit + fd3/4 pipe) |
| | JsonRpcLineTransport(@deepseek-ai/dsh-sdk-protocol) |
| | initialize / session/prompt / session/cancel / shutdown |
| | session.event / session.status / subagent.* 通知转发 |
| | 审批 / ask_user 双向请求;模型、权限、压缩、回溯扩展 |
| +- dsh-tui(Rust/ratatui,TTY 归 TUI,fd3/4 走协议) |
+------------------------------------------------------------------+
+------ standalone(未来)------------------------------------------+
| dsh-tui --runtime-bin <bin>:自己 spawn SDK runtime 子进程 |
+------------------------------------------------------------------+
构建与运行
cargo build # 编译
cargo run -- --demo # 脚本化 demo(无需 runtime/API key)
cargo run -- --demo --theme light
cargo run -- --dump-frame 100x30 # 无 TTY 的确定性布局检查
插件模式(需要全局 dsh 0.1.0-rc.6,一条命令装好):
npm install -g @deepseek-ai/dsh@0.1.0-rc.6 # 装到 /opt/homebrew/bin(或你的 npm prefix)
scripts/build-npm.sh # cargo release + stage vendor 二进制 + npm pack
dsh plugin --profile tui add ./dist/*.tgz # 安装到 tui profile
dsh --profile tui # 启动
终端适配
启动时探测终端(TERM_PROGRAM/TMUX):VS Code 家族自动把退出键提示换成 Ctrl+D(宿主抢占 Ctrl+Q),状态栏显示终端名与 tmux 状态;色彩按终端能力量化(truecolor/256/16)。
社区发现
仓库带 dsh-plugin topic;发布 npm 后可被 awesome-dsh-plugin 精选列表与 dsh-find-plugin 检索收录。
模型默认路由
TUI 的默认 provider/model 从 ~/.dsh/settings.yaml 的 dsh-whale-tui: 块读取(缺省回退到全局 agent-default-model:,再回退 stock):
dsh-whale-tui:
provider: opencode-go
model: deepseek-v4-flash
命令行 --provider / --model 优先级最高。
键位
| 键 |
行为 |
| Enter |
单行模式:空闲发送、turn 中排队;多行模式:换行 |
| Alt+Enter |
发送多行输入;turn 中先取消当前 turn 再发送 |
| Ctrl+M |
prompt 聚焦时切换多行模式;scrollback 聚焦时打开模型选择器 |
| Shift+Enter |
不切模式直接插入换行 |
| Esc |
turn 中取消;空闲时双击清空 / 双击 rewind(800ms 窗口) |
| Ctrl+C |
先清草稿,再按取消 |
| Tab |
scrollback 与 prompt 焦点切换 |
| ← / → |
移动光标;Alt+← / Alt+→(或 Alt+B / Alt+F)按词移动 |
| Ctrl+A / Ctrl+E,Home / End |
行首 / 行尾(Ctrl+E 在 scrollback 聚焦时仍是折叠全部 thinking) |
| Ctrl+W,Alt+Backspace |
删除光标前一个词;Alt+D 删除光标后一个词 |
| Ctrl+U / Ctrl+K |
删到行首 / 行尾 |
| Ctrl+Z |
撤销上一次编辑(连续输入合并为一步) |
| Delete |
删除光标处字符 |
| ↑ / ↓ |
prompt 中:多行草稿内换行移动,单行或首/末行时浏览输入历史;scrollback 中选择条目 |
| h / ←,l / →,e |
折叠、展开、切换选中条目 |
| g / G,Home / End |
跳到首个 / 最后一个条目 |
| Shift+H / Shift+L |
上一 / 下一个 turn(用户提问) |
| Shift+K / Shift+J |
上一 / 下一条 assistant 回复 |
| Ctrl+K / Ctrl+J |
上 / 下滚一行(不动选中) |
| Ctrl+U / Ctrl+D |
上 / 下滚半页(scrollback 聚焦时;composer 里 Ctrl+D 是退出) |
| Shift+E |
全部折叠 / 全部展开 |
| Enter / Ctrl+F |
全屏查看选中块 |
| Ctrl+O |
切换 always-approve |
| Shift+Tab |
循环切换 Normal / Plan / Always-approve 权限模式 |
| PageUp / PageDown,鼠标滚轮 |
翻动会话视口 |
| Ctrl+E |
scrollback 聚焦:折叠 / 展开全部 thinking(prompt 聚焦时是行尾) |
| Ctrl+T |
todos 面板:agent 任务清单快照(y 复制 · q/Esc 关闭) |
| Ctrl+Q ×2 |
退出(双击确认;composer 内 Ctrl+D 同义) |
| Ctrl+N ×2 |
新会话(双击确认,会丢弃当前上下文) |
| Ctrl+P / ? |
命令面板(slash 命令 + 常用操作,可过滤) |
| Ctrl+X / Ctrl+. |
快捷键速查 |
| Ctrl+G |
tasks 面板:后台任务 + 活跃子代理(r 刷新) |
| z(问题卡内) |
自由文本回答(Enter 提交 · Esc 返回选项) |
| y / Y |
复制选中块内容 / 元数据(剪贴板三路由:native→tmux→OSC52,备份 ~/.dsh/last-copy.txt) |
| 选中/复制文字 |
默认就能用 —— 鼠标上报默认关闭,选区归终端 |
/mouse |
开启滚轮滚动与点击切焦点。开启后终端把鼠标交给程序,选区要按住 Shift(并非所有终端支持)。只开按键上报(?1000h+?1006h),不开 motion 上报 |
| 滚动(不用鼠标) |
PageUp / PageDown、Ctrl+U / Ctrl+D 半页、Ctrl+J / Ctrl+K 单行、j / k、g / G |
Slash 命令(已实现)
| 命令 |
行为 |
| /resume |
会话选择器:直读 ~/.dsh/sessions 的 JSONL(zstd 多帧),Enter 恢复并回放完整历史,后续消息走 agents.resume 的活会话 |
| /new (/clear) |
新会话 |
| /exit (/quit) |
退出 |
| /help |
命令列表 |
| /session-info (/context /status /info) |
会话明细弹窗:模型/目录/turn 数/token 用量 |
| /theme |
主题实时预览(方向键切换预览 · Enter 保持 · Esc 还原) |
| /copy |
复制最近回复 |
| /model |
模型选择器 |
| /compact |
压缩当前会话历史 |
目录
src/
main.rs CLI、终端守卫、事件循环
bus.rs AppEvent / Cmd
proto.rs NDJSON JSON-RPC(attach/spawn、request/notify、超时、kill/shutdown)
app.rs 应用状态:RunState、Esc 状态机、follow-up 队列、焦点
transcript.rs 会话事件 → 渲染 Cell(消息/思考/工具卡)
ui.rs ratatui 渲染:状态栏/scrollback/输入框/快捷键栏
theme.rs grok 式颜色槽位(深/浅两主题)
demo.rs 脚本化 demo 流
npm/ TS 桥插件(cordis.patch.yml + lib/index.js + bin 入口)
scripts/ 构建与打包(build-npm.sh / package-native.mjs)
实现状态
P1 闭环已落地:真实会话、事件流、审批与问题卡、模型/权限切换、恢复/回溯、压缩、任务与子代理视图均通过同一 fd3/fd4 JSON-RPC 通道运行。--dump-frame 用于确定性布局检查,插件模式用 dsh --profile tui 做真实会话验证。