# dsh-TUI

> DeepSeek Harness 的 Claude Code 风格终端前端，含流式 Markdown、工具卡、`/resume`、会话 fork 与全屏渲染。

## 元数据

- 作者: [@ccch1mneyyy](https://github.com/ccch1mneyyy)
- 仓库: <https://github.com/ccch1mneyyy/dsh-TUI.git>
- GitHub: [ccch1mneyyy/dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI)
- Star: 1,797
- 主语言: TypeScript
- License: [MIT](https://spdx.org/licenses/MIT.html)
- 主页: <https://dshtui.com/>
- Topics: `claude-code`, `coding-agent`, `deepseek`, `deepseek-harness`, `dsh-plugin`, `ink`, `react`, `terminal`, `tui`
- Fork: 78
- Open Issues: 56
- 最后推送: 2026-08-17T16:43:00.000Z
- 加入目录: 2026-08-14T00:00:00.000Z

## 安装

```bash
dsh plugin --profile web add github:ccch1mneyyy/dsh-TUI
```

## 百科

## 一句话定位
dsh-TUI 是 DeepSeek Harness 的 Claude Code 风格终端交互前端，作为 Cordis bundle 插件挂在 dsh-base 之上，复用官方 Agent、模型、会话和工具服务，向用户提供流式 Markdown 对话、`/resume`/`/new`/模型切换、双击 Esc 回溯和全屏 alt-screen 渲染。

## 核心能力
- 在终端内提供流式 Markdown、结构化工具卡、命令与文件补全、`@` 文件引用（任意位置补全、图片发为持久附件）、历史搜索、消息选择，并支持 inline 与 alt-screen 两种渲染模式
- 在状态栏实时显示工作进度、分段上下文用量、TPS、缓存命中率、推理等级、输入/输出 token 与 Git/会话信息
- 提供完整的会话工作流：`/resume` 会话浏览器、`/new` 新会话、`/compact` 压缩、`/export` Markdown 导出、`/btw` 侧问、模型切换，以及空输入双击 `Esc` 通过 session fork 进行时间回溯
- 接入 DSH 官方能力：Agent preset 名册（`/preset`）、Skills（`/audit` `/bug` `/review` 等）、MCP（`/mcp`）、Goals、Todos、子代理和 `ask_user_question` 问卷
- 为长会话做了事件驱动投影、差分终端输出、消息虚拟化、回放合并与有界缓存，避免渲染与内存随会话无限增长
- `/theme` 内置 `auto`/`light`/`dark`/`dark-ansi` 四种主题并支持 `~/.dsh-tui/themes/<名字>.json` 自定义，`/lang` 中英界面切换，全部走 Cordis 配置 + 持久化 + env 三级覆盖

## 技术实现
- **语言**: TypeScript（ESM，`"type": "module"`，`tsc` 编译到 `lib/types/`）
- **关键依赖**: `react@^19.2.0`、`react-reconciler@^0.33.0`、`@deepseek-ai/cordis@^4.0.1`、`@deepseek-ai/dsh-agent@^0.1.0-rc.6`
- **架构模式**: 单一 Cordis 插件 `name: 'dsh-tui'`，通过 `dsh.bundle.patch → ./cordis.patch.yml` 覆盖 `dsh-base` 默认行（禁用 host 层工具/计划/压缩/技能等，由 Agent preset 名册接管），官方 `@deepseek-ai/*` 导入集中在 `src/dsh-adapter/` 边界之内，渲染基于移植的 Ink+Yoga（差分输出、终端能力探测、ConPTY 兼容路径）
- **入口文件**: `src/index.ts`（re-export shim）→ `src/dsh-adapter/index.ts`（`Config` Schema + `apply`）→ `src/dsh-adapter/plugin.tsx`（运行时实现），bin 入口为 `bin/dsh-tui.js`（一键探测 dsh/pnpm 并自举 profile）

## 适用场景
当你想把 DSH 用成"日常开发对话伙伴"——在终端里持续多轮对话、需要看思考与工具调用的可视化、随时回查或回溯历史会话、不希望被网页打断时，dsh-TUI 提供 Claude Code 熟悉的交互体验。同时它和官方 web 端共用 session/event 真源，所以 TUI 里开的会话也可以在 web 上接着看。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DeepSeek Harness | 0.1.0-rc.6+ | 由 `package.json#peerDependencies` 锁定的众多 `@deepseek-ai/dsh-*` 决定 |
| Node.js | ^22.19.0 \|\| >=24.0.0 | `package.json#engines.node`；CI 使用 24 |
| pnpm | 10+（推荐 11+） | profile 内包安装交给 pnpm；pnpm 9 传递依赖不提升会立即退出（issue #60） |
| 平台 | macOS / Windows / Linux | 跨平台；Windows 走 `dsh-tui.cmd`、剪贴板读 PowerShell、sandbox 在 Windows 上不可用故退到 `danger-full-access` |
| 原生模块 | 无 | 全 JS/TS 依赖，无需 `node-gyp` 构建 |
| Peer 依赖 | @deepseek-ai/cordis ^4.0.1 与全套 @deepseek-ai/dsh-* ^0.1.0-rc.6 | 由宿主 DSH 提供；卸载请保证宿主版本对齐 |
| 必需凭证 | `DEEPSEEK_API_KEY`（自定义端点再加 `DEEPSEEK_BASE_URL`） | 见 `cordis.patch.yml:28-30` |

## 安装方式
```bash
dsh plugin --profile web add github:ccch1mneyyy/dsh-TUI
```

## 配置项
dsh-TUI 的所有可调参数都集中在 `Config` Schema（`src/dsh-adapter/index.ts:87`）。在 `$DSH_HOME/profiles/dsh-tui/cordis.patch.yml` 里以 `dsh-tui` 行的 `config` 块整段替换，整体改动一行完成；常用项如下：

| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| `sessionId` | 字符串 | 启动时恢复的已有会话 ID；留空则建新会话 | 未设置 |
| `provider` | 字符串 | LLM 路由名称；与 `model` 同时配置才视为显式路由 | Harness `agentDefaultModel` |
| `model` | 字符串 | 启动模型；`/model` 通过 session fork 切换并写回持久化 | Harness `agentDefaultModel` |
| `cwd` | 字符串 | 会话侧工作目录（Agent meta、`@` 补全、`/resume` 过滤、状态栏）；建议显式绝对路径 | 启动目录所在的 git worktree 根 |
| `workspace` | 字符串 | 启动时解析的工作区目标：本地绝对路径、`file://` URI 或插件注册 URI | 未设置 |
| `effort` | 字符串 | 每请求实际生效的推理等级（deepseek 仅 `off`/`high`/`max`，非法静默回落） | `max` |
| `activity` | 布尔 | 是否显示实时工作状态行 | `true` |
| `activityFrames` | 字符串 | 工作状态动画预设（`claude`/`moon`/`comet`/`dots`/`random`） | `claude` |
| `contextBar` | 布尔 | 是否显示输入框下方的分段上下文进度条 | `true` |
| `fullscreen` | 布尔 | `true` 用 alternate screen + 鼠标选区；`false` 用 inline | `false` |
| `lang` | en \| zh | 界面语言（被 `DSH_TUI_LANG` 覆盖） | `zh` |
| `preset` | 字符串 | 新会话 Agent preset（`standard`/`code`/`minimal`/`cordis`/`liangshen`） | 持久化或 `standard` |
| `diffLayout` | auto \| split \| unified | 文件编辑 diff 的展示方式（`auto` 按终端宽度选） | `auto` |
| `modes` | 数组 | `Shift+Tab` 会话模式循环的原子组合（plan/sandbox/approval） | 内置三档默认→plan→full |

`Config` 不直接暴露的主题/语言/调试等开关走环境变量（优先级：env > `config` > 持久化 > 默认）：

| 环境变量 | 说明 |
|---|---|
| `DSH_TUI_LANG` | 锁定界面语言 `en`/`zh` |
| `DSH_TUI_THEME` | 锁定内置或自定义主题，优先于持久化选择 |
| `DSH_TUI_PERSONA` | 覆盖 Agent persona（不设则为 `You are a coding agent.`） |
| `DSH_TUI_PRESET` | 覆盖新会话默认 Agent preset |
| `DSH_TUI_DISABLE_MOUSE` | fullscreen 模式下临时关闭鼠标捕获 |
| `DSH_TUI_SESSION_ROOT` | 覆盖 JSONL 会话根目录 |
| `DSH_TUI_RESUME_SESSION` | 启动时恢复指定会话（启动器通常帮你设） |
| `DSH_TUI_WORKSPACE_TARGET` | `dsh-tui <目标>` 触发的启动工作区 |
| `DSH_TUI_WORKSPACE` | Windows `dsh-tui.cmd` 采用的工作目录 |
| `DSH_TUI_DEBUG` | 启用 stderr 调试日志（不要打日志到 stdout，会破界面） |
| `DSH_TUI_RENDER_LOG` | 指定文件路径落原始 ANSI 帧用于取证（含可见内容，慎传） |
| `DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL` | 模型凭证与自定义端点 |
| `DSH_PERMISSION_MODE` | 非 Windows 平台覆盖 sandbox policy（`workspace-write`/`danger-full-access`） |

旧名 `CC_TUI_*` 与 `DSH_CC_*` 自当前版本起不再生效（启动器会逐次警告），唯一例外是 resume 写双路径（`DSH_TUI_RESUME_SESSION` 与 `DSH_CC_RESUME_SESSION` 同时设置）。

## 常见问题
**Q: 我用的是 pnpm 9，装完启动后立刻退回 shell、几乎没报错。**

A: 这是 issue #60 的典型表现：pnpm 9 安装 profile 时不会把传递依赖 `dsh-working-activity` 提升到 loader 可解析位置，模块解析失败导致整棵插件树被回收，TUI 打完 resume 提示后直接退出。升 pnpm 到 10 或 11 即可：`npm install -g pnpm@latest && dsh plugin --profile dsh-tui add @deepseek-harness-tui/dsh-tui@latest`。

**Q: 启动器报"无法启动：profile 内运行的是 v…，而启动器是 v…"。**

A: 这是 issue #183 的反向错位保护：全局启动器比 profile 内包新一个 minor 版本时，CLI 会用启动器的 bundle patch 套到旧包上，子路径导出可能解析不到，必然模块解析崩溃。让两者对齐：`dsh plugin --profile dsh-tui add @deepseek-harness-tui/dsh-tui@<启动器版本>`（或统一升 `@latest`）。

**Q: `/model` 选了别的模型后我想回到原来那条对话，怎么办？**

A: 模型切换在 TUI 里是 session fork 续聊，不修改历史。旧会话完整留在 `/resume` 列表里；选择本身持久化在 `~/.dsh-tui/model.json`，下次 `/new` 与重启也沿用。

**Q: 装了一遍版本看着没变？**

A: 不带 `@latest` 时 pnpm 按 profile `package.json` 里已记录的版本范围（如 `^0.1.4`）就地解析，会停留在旧的主线上。显式指定：`dsh plugin --profile dsh-tui add @deepseek-harness-tui/dsh-tui@latest`，启动后看顶栏右上角的 `vX.Y.Z` 验证。

**Q: 状态栏的工作状态行有两个 / 不显示。**

A: 重复多半是 profile 里另外加了 `dsh-working-activity`（README 与 docs/getting-started.md 都警告不要重复 add）。移除 profile 里的重复 bundle 配置、只保留本包 patch 自动插入的 `working-activity` 行即可。如果不显示，检查 `cordis.yml` 中 `dsh-tui` 的 `activity: true` 是否被覆写、`/activity` 选择是否误关，或用 `DSH_TUI_DEBUG=1 dsh --profile dsh-tui` 看 stderr。

**Q: 卸载插件会不会丢会话数据/改动 DSH 核心？**

A: 不会。dsh-TUI 是纯插件挂载（README.md:21），不修改 DSH 核心；卸载即还原。主题/语言/preset 等偏好继续留在 `~/.dsh-tui/` 下，需要时可手动删。会话日志仍在 `$DSH_HOME/sessions/`（profile）或 `~/.dsh-tui/sessions/`（裸 `cordis.yml`）的原路径。

**Q: Windows 上粘贴图片为什么不像文档里写的一样发为图片块？**

A: Windows 走 PowerShell `Get-Clipboard` 读剪贴板。剪贴板被其他程序锁定时会先重试、最终若仍被锁则静默降级为临时文件路径插入（不内嵌 base64）；Finder 多文件复制在 macOS 没有稳定的 AppleScript 读法，会按文本/图片回退。这些都是 README 与 architecture 文档列出的"已知限制"。

**Q: 终端不支持 alt-screen 或想关闭鼠标怎么办？**

A: 把 `cordis.patch.yml` 里 `dsh-tui` 的 `fullscreen: false`，或单次启动用 `DSH_TUI_DISABLE_MOUSE=1 dsh --profile dsh-tui` 临时关鼠标。Terminal.app（macOS 自带）会消费 `⌘` 组合，请在它里面继续用 `Ctrl+<键>`（iterm2/kitty/WezTerm/ghostty 等才支持 `⌘`）。

## 上手难度
入门 — 默认配置可直接启动；想做主题、Agent preset、MCP、审批策略调优时再进入配置参考。

## 已知问题与限制
- **Windows 没有可用 sandbox 后端**：profile 在 Windows 上默认 `danger-full-access` 且审批策略为 `never`；不可信仓库里跑前请先检查实际 patch 与 `DSH_PERMISSION_MODE` 设置（docs/architecture.md:110-111 / cordis.patch.yml:134）。
- **`/model` 通过 session fork 续聊**：不原位替换；旧会话留在 `/resume` 列表里（docs/architecture.md:122-123 / README.md:225-228）。
- **`Ctrl+V` 剪贴板按平台分派**：Windows 用 PowerShell `Get-Clipboard`（被锁时先重试仍失败会静默降级），macOS 用 `osascript`/`pbpaste`（Finder 多文件复制没有稳定 AppleScript 读法），Linux 按顺序探测 `wl-paste`/`xclip`/`xsel`（全部不可用即报错）；剪贴板位图以临时文件路径插入而非内嵌为图片块（README.md:229-234）。
- **注入到 system prompt 的插件上下文不独立展示**：会并入上下文进度条统计（docs/architecture.md:121-122 / README.md:225）。
- **退出路径不等待 Agent 异步落盘**：以进程退出收尾，持久化插件兜底（README.md:235 / docs/architecture.md:129）。
- **`/vim`、`/connect`、`/hooks` 是占位命令**：DSH 侧无等价机制时会给出明确说明而非静默（README.md:240-241 / docs/architecture.md:133）。
- **裸 `cordis.yml` 启动没有 `/permission` 预设切换**：只有 profile 组合（`dsh-base` 的 `permission-presets` 行默认挂载）才有（README.md:236-239 / docs/architecture.md:131-132）。
- **旧名环境变量兼容**：旧 `CC_TUI_*` 与 `DSH_CC_*` 不再生效（启动器每次仍设到时会告警），唯一例外是 resume 写双路径（`DSH_TUI_RESUME_SESSION` 与 `DSH_CC_RESUME_SESSION` 同时设置保证旧启动器可用）（docs/getting-started.md:79-92 / src/utils/paths.ts:58-68）。
- **数据目录迁移只复制不移动**：首次启动若 `~/.dsh-cc` 存在而 `~/.dsh-tui` 不存在，会整体复制到新目录并提示一行；旧目录留给你确认新目录正常后手动删除（docs/getting-started.md:88-90 / docs/architecture.md:96-98）。
- **`sessionHistory` 仍兼容旧路径 `~/.dsh-cc/resume.txt`**：`src/sessionHistory.ts:14` 标 `TODO: drop the legacy path once pre-rename users migrate`，当前版本还需保留双写兜底。
- **Ink 渲染层有未结 TODO**：移植的 Ink 子模块在 `src/ink/screen.ts:805`（软换行未实现时 SpacerHead cell 处理）与 `src/ink/render-to-screen.ts:172`（cell 转换待抽公共 helper）两处标 TODO，等代码稳定后跟进。

---

本文档由 [deepseek-plugin.org](https://deepseek-plugin.org) 自动生成，对应 HTML 页面: [dsh-TUI](https://deepseek-plugin.org/plugins/ccch1mneyyy/dsh-TUI)
百度百科由 AI 生成 (模型: `MiniMax-M3`)
