界面一览
┌──────────────────────────────────────────────────────────────┐ │ ~/github (Full access) │ ⎇ main ◄─ 编辑框上沿信息条 │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 对话流(可滚动,只留干净问答正文) │ │ │ │ ▸ You 这个报错帮我看看 │ │ │ │ ● AI ## 排查结论 │ │ │ │ 1. 根因是 …… │ │ │ └──────────────────────────────────────────────────────┘ │ │ ● Todos (1/3) ◄─ 待办面板(做完自动收起) │ │ 💭 thinking · 12.4s ◄─ 思考面板 │ │ ⚙ read session.ts ◄─ 工具面板 │ │ ❯ _ │ ├──────────────────────────────────────────────────────────────┤ │ ├─ ⠋ 牛马狗 · 21k/1m · round 2/75 · 13.6s · reading files… │ │ └─ ⠋ 小黄鸭 · … ◄─ 运行中的子代理行 │ │ ● 正在请求… │ ├──────────────────────────────────────────────────────────────┤ │ dsh(standard)▸☁️deepseek▸🤖gpt-5◕▸🧠32k/128k(25%)▸💬12▸🔧34▸21:03:44 │ ⌨ Enter 发送 · Esc×2 停止 · Ctrl+G 子代理 · Tab preset │ └──────────────────────────────────────────────────────────────┘
设计取舍:过程细节(think/tool/todo)从对话流里抽出来钉在固定位置,主对话流永远只有干净的问答正文;状态则全部沉到屏幕边缘的底栏与信息条。
UI 拆解
Powerline 底栏
底栏是经典 powerline 样式,所有数字都是 O(1) 维护的计数器——渲染路径从不扫描会话日志,再长的会话也不掉帧:
dsh(standard) ▸ ☁️ deepseek ▸ 🤖 gpt-5 ◕ ▸ 🧠 32k/128k (25%) ▸ 💬 12 msgs ▸ 🔧 34 tools ▸ 21:03:44 └品牌+preset──┘ └─提供商──┘ └模型+思考等级┘ └─上下文用量(分档变色)─┘ └消息数─┘ └工具数┘ └时钟┘
上下文用量按阈值分四档变色:50% 内正常、50%/70%/90% 逐级转橙转红,快满了瞄一眼就知道。编辑框上沿还有一条信息行:当前目录、权限徽标、git 分支:
~/github (Full access) │ ⎇ main └─cwd──┘ └─权限徽标──┘ └分支
底栏下面还有一条可开关的快捷键提示行,7 个段落各自可在设置里单独关掉,全关后一行都不占:
⌨ Enter: send · Esc ×2: stop · Ctrl+C ×2: quit · Ctrl+D: quit (empty) · Ctrl+G: subagents · Tab: preset · ↑↓: history
Think / Tool 面板
思考与工具各一块面板,钉在输入框上方,高度一个设置搞定(dsh-tui.panelHeight):
'1'(默认) 💭 thinking · 12.4s · 正在分析调用栈……
└ 一行极简:标识 + 耗时 + 最新一行,超宽右截断
'5'|'7'|'10' ┌─ 💭 thinking ──────────────────┐
│ 盒装面板,正文 N-1 行 │
└────────────────────────────────┘
'all' 全量展示;流式思考保留 200 行活动尾部,
工具结果封顶 2000 行,超出显示 … (+N lines)
工具面板头部自带状态:⚙ 执行中 / ✔ 成功 / ✘ 失败 + 工具名 + 操作对象 + 耗时,成败还有不同底色;派活类操作(use_agent 等)不占工具面板,它们有自己的视图(见下)。面板自绘、随宽度自适应重排,换主题只需重画一帧。
Todo 面板
任务清单以带边框表格呈现在输入框正上方,编号、勾选态、内容三列,三种状态三种颜色:
● Todos (1/3) ───────────────────────────────── # ✓ Task 1 ☑ 定位根因 2 ◐ 补回归测试 ◄ 进行中 3 ☐ 更新文档
列表为空或全部完成时整个面板自动收起,不留空壳。
子代理:运行行 + 洞察视图
每个运行中的子代理在输入框下方占一行,旋转指示器 + 名称 + 当前上下文用量 + 轮次 + 已耗时 + 最新输出尾巴,一眼看清谁在干什么:
● 帮我把这个 bug 修了 ◄ 最近一次请求(常驻) ├─ ⠋ 牛马狗 · ↻1 · 21k/1m · round 2/75 · 13.6s · reading src/session.ts… └─ ⠋ 小黄鸭 · 8k/1m · round 1 · 4.2s · 已识别报错堆栈中的……
按 Ctrl+G(或 /subagents)进入洞察视图,两层结构:
Ctrl+G ──► ● Sub-agents 选择层(运行中在前,最近完成 5 个)
│ <Xk tok> · 🧹 N次压缩 · 耗时 —— 每行的摘要栏
▼ Enter
● 单个子代理的实时转录层
🐳 回复 ⚙ 工具调用 ✔✘ 结果 ▎用户 🧹 DCP压缩 ☑ todos
尾部跟随刷新,向上滚动即脱离跟随翻历史
并发上限、轮次上限都能调(/agents 里配置:默认并发 4、每轮 75 轮)。
Reset 的四种姿势
「重来」在这个 TUI 里分场景各有其键:
| 场景 | 操作 | 行为 |
|---|---|---|
| 停掉正在跑的任务 | Esc 两连(500ms 内) |
连同子代理一起停 |
| 中断本轮回复 | Ctrl+C(运行中) |
取消当前 turn |
| 清空输入框 | Ctrl+C(空闲时) |
编辑器文本一键清零 |
| 开新会话 | /new |
脱离当前会话,清屏清面板 |
另外 /settings 浏览器里每组配置都有 Reset to defaults 行(二次确认),/hotkeys 编辑器里把按键清空即恢复默认绑定。
命令全家福
TUI 自带命令全部双通道注册(无会话时也能直接执行),dsh 内置命令则原样透传、绝不重新发明:
| 命令 | 说明 |
|---|---|
/model |
选模型(含思考等级) |
/think |
切换当前模型的推理等级 |
/agents |
管理本地 markdown 定义的 agent(模型、思考等级、派生深度) |
/subagents |
浏览子代理并查看其实时转录 |
/preset [name] |
切换 agent preset(Tab 也可循环) |
/theme |
切配色方案,立即生效 |
/session |
查看当前会话信息(id、模型、统计) |
/resume |
恢复一个已持久化的会话 |
/export [path] |
导出会话日志为 JSONL |
/new |
开新会话 |
/settings |
浏览器式配置编辑(命名空间、取值、重置) |
/skills |
管理用户技能(已装 + 可装) |
/login [provider] |
配置 provider API key(可搜索目录 + 打码密钥输入) |
/logout |
登出 provider(密钥与配置一并移除,模型立刻消失) |
/permission |
权限预设选择器 |
/model-sync |
发现手写 baseURL provider 的可用模型并入配置 |
/hotkeys |
查看/自定义快捷键(~/.dsh/keybindings.json) |
/reload |
从源码热重载 TUI |
其中登录/登出是完整的图形化流程:/login 是可搜索的 provider 目录,API key 输入框逐字符打码显示;/logout 会列出所有存有凭据的 provider 供选择摘除。技能、计划、压缩等 dsh 原生命令以及你自己装的 skill 都会自动出现在补全列表里。
Agent Preset
Tab 循环切换,/preset 弹选择器,/preset <名字> 直达,/preset next 下一个;当前 preset 直接显示在底栏品牌段上:
Tab ──► code ⇄ cordis ⇄ minimal ⇄ [standard] ──► 下一个会话生效
底栏同步显示 dsh(standard)
启动时默认选中 standard;没碰过 preset 时不下发任何预设参数,服务端配置的默认值说了算。
主题引擎
GitHub light / dark 双配色;auto 模式跟随终端明暗,运行中切换也实时跟进:
/theme ──► settings 变更 ──► applyThemeRef
├─► 历史消息 ReplayOp 整屏换装
└─► 画布背景换色 + 输入区重建
(一帧节流完成,无残影)
画布背景逐行涂色(BCE),在 cmux/gostty 这类终端里切主题也不会留下冻住的默认底色;想要透明背景,DSH_TUI_TRANSPARENT=1 一键退回。
子代理:注册中心 + 三件套
配套插件 dsh-subagent-registry(https://github.com/fan56/dsh-subagent-registry)把本地定义的自定义 agent 注册成可调用的 subagent:扫一个 agents 目录,每个 markdown 就是一个人设,主模型通过统一的 use_agent 工具按名字派活。招牌能力是断点续跑——跑了一半被中断的子代理,下次调用会从已保存的部分成果接着来,而不是从头再来。
我日常挂三个:
| 名字 | 一句话定义 |
|---|---|
| 🦆 小黄鸭 rubber-duck | 多模态视觉 agent:替你看截图、图表、报错界面并输出结构化结论,也能把数据画成图——主模型是 text-only 时它就是眼睛 |
| 🐕 牛马狗 workhorse | 干活的主力:写代码、调查、测试、部署全包,受安全门约束——不出 $HOME、不碰危险命令 |
| 🧙 老法师 oldfox | 解决难题的顾问:分析、排障、review、挑刺,专门给方案和代码把关,不做设计不动手 |
它们在 TUI 里是一等公民:运行行、洞察视图、🧹 压缩账目全都照常工作。
上下文压缩:DCP
配套插件 dsh-dcp(https://github.com/fan56/dsh-dcp)把默认的「LLM 总结式压缩」换成确定性的上下文剪枝:压缩全程零模型调用,同样输入永远得到同样输出,路径/命令/报错原文/未完成 todo 这些硬事实逐字保留。实测约 8 万 token 的历史压到几百 token。TUI 里每次压缩以 🧹 标记呈现,子代理各自压了几次也有账可查。
中文安全
截断统一走宽度词汇表:CJK 全角 = 2 列、grapheme 永不劈半、ANSI 先裁后染。中文内容随便贴,不会错位串行。
质量
- 零上游补丁:所有能力纯插件挂载,升级 dsh 不用重新合代码
- 544 个测试(37 个文件)持续护航;e2e 覆盖 24 行小终端场景
- 同系列还有网关网络错误的有界重试插件 dsh-llm-net-retry(https://github.com/fan56/dsh-llm-net-retry),按需取用
安装
$ dsh plugin --profile tui add @aiwayds/dsh-tui-pi $ dsh --profile tui
更多配置与排障见 👉 https://github.com/fan56/dsh-tui-pi