Bundle
dsh-simple-cli
Interactive terminal chat bundle for dsh: drives an Agent from stdin over dsh-base.
- Source
- kit-zeason
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 7 days ago
Readme
# dsh-simple-cli — Interactive Terminal Profile for DeepSeek Harness
> **v0.1.2** - [Releases](https://github.com/kit-zeason/dsh-simple-CLI/releases) · [Changelog](CHANGELOG.md)
`dsh-simple-cli` turns [DeepSeek Harness](https://www.npmjs.com/package/@deepseek-ai/dsh) (`dsh`) into an interactive terminal chat, like Claude Code: type a message, watch the answer stream in, keep the conversation going. It is launched as the **`dsh-cli`** command (equivalent to `dsh --profile cli`).
**English** · [中文](#中文)
---
## What it is
`dsh` ships with web / headless profiles. This repo adds a **`cli` profile** that runs entirely in the terminal, Claude-Code style:
- **Streaming output with reasoning & tool calls** — assistant text, model reasoning (`▸ 推理`, dim gray) and tool invocations (`⛭ name: args` + `✓/✗` result) stream in live, matching the TUI experience. Reasoning is collapsed to its **latest line** while thinking (web-style) and renders are coalesced to ~30fps, so fast thinking does not flicker the terminal
- **`@` file picker** — type `@` in the input to browse the working directory (type to filter, ↑/↓ navigate, **Enter** enters a folder or picks a file, **Esc** closes); picking inserts `@relative/path`, and on submit each referenced text file is attached to the message (up to 64 KB each, truncated with a note; binary files stay as a path mention)
- **`──` input area** — a separator line frames the input box; **Enter** submits, **Shift+Enter / Ctrl+Enter** insert a newline (multi-line messages; requires the kitty keyboard protocol — Windows Terminal 1.19+, kitty, WezTerm, iTerm2, Konsole, foot — otherwise **Ctrl+J** or **Alt+Enter** work as a fallback), ↑/↓ navigate history or logical lines
- **Status bar** — under the input: current model & permission, with the switching commands always in view
- **Continuous conversation** — multi-turn context on a single Agent session
- **Tool-call approvals** — inline `y` / `n` / Enter on the same input area under the "ask" policy
- **Commands** — `/new` fresh session · `/model [provider/]<model>` switch model (bare `/model` opens a filterable picker) · `/preset [id]` switch agent preset (bare `/preset` opens a filterable picker) · `/permission ask|never` switch approval policy · `/clear` · `/exit`; an optional first message can be passed as an argument
- **Default color scheme** — every role has its own color (user green, reasoning gray, tools yellow/cyan…); override via the `DSH_CLI_THEME` env var (see below)
## Requirements
- Node.js ≥ 22 (tested on 24)
- `dsh` ≥ 0.1.0-rc.6 on PATH: `npm i -g @deepseek-ai/dsh`
- PowerShell 5.1+ / pwsh for `install.ps1` (Windows & Unix)
## Install
### One-click (Windows & Unix, PowerShell)
```powershell
git clone https://github.com/kit-zeason/dsh-simple-CLI.git
cd dsh-simple-CLI
.\install.ps1 # install into $DSH_HOME/profiles/cli, shim onto PATH, verify
.\install.ps1 -Force # re-install / overwrite existing profile files
```
The installer:
1. creates `$DSH_HOME/profiles/cli` (default `~/.dsh/profiles/cli`)
2. copies the `dsh-simple-cli` bundle into the profile's `node_modules`
3. writes the profile manifest (`package.json`, `cordis.patch.yml`, `pnpm-workspace.yaml`)
4. installs the global `dsh-cli` command shim
5. verifies `dsh --profile cli --help`
No git clone needed — install a specific published release directly:
```powershell
Invoke-WebRequest -Uri https://raw.githubusercontent.com/kit-zeason/dsh-simple-CLI/main/install.ps1 -OutFile install.ps1
.\install.ps1 -Version 0.1.0 # download release v0.1.0 and install it
.\install.ps1 -Version 0.1.0 -Force # re-install, overwriting existing profile files
```
`-Version` accepts `0.1.0` or `v0.1.0`; without it, the script installs the
checkout it is part of (e.g. your clone of `main`).
### Manual (any OS)
```powershell
$profiles = Join-Path $env:USERPROFILE '.dsh\profiles'
New-Item -ItemType Directory -Force -Path "$profiles\cli\node_modules\dsh-simple-cli\lib"
Copy-Item package.json, cordis.patch.yml "$profiles\cli\node_modules\dsh-simple-cli\"
Copy-Item lib\index.js, lib\startup.js "$profiles\cli\node_modules\dsh-simple-cli\lib\"
Copy-Item profile\package.json, profile\cordis.patch.yml, profile\pnpm-workspace.yaml "$profiles\cli\"
# optional: put bin\dsh-cli(.cmd/.ps1) somewhere on PATH
```
## Usage
```powershell
dsh-cli # start interactive chat
dsh --profile cli # same
dsh-cli "run the tests" # send a first task, then keep chatting
```
```
┌───────────────────────────────────────────────┐
│ dsh-cli: DeepSeek Harness 交互模式(/help …) │
│ ▸ 推理 let me check the repo layout… │ ← dim gray, streaming
│ ⛭ pwsh: Get-ChildItem -Recurse . │ ← yellow
│ ✓ Directory: … │ ← cyan
│ 这里是最新的文件清单:… │ ← streaming reply
│───────────────────────────────────────────────│
│ > 输入你的消息… (Shift+Enter 换行, Enter 发送) │ ← input area
│ 模型: deepseek/deepseek-v4-flash · 权限: ask … │ ← status bar
└───────────────────────────────────────────────┘
```
Commands (type them in the input area):
| Command | Effect |
| --- | --- |
| `/model` | open the model picker (browse or keyword-search, then pick) |
| `/model <name>` | switch model (same provider), e.g. `/model deepseek-v4-flash` |
| `/model <provider>/<name>` | switch provider + model |
| `/preset` | open the preset picker (标准 / PTC / 极简 / 创造 + your own) |
| `/preset <id>` | switch preset (live on a blank session, else applies on `/new`) |
| `/permission ask` / `never` | switch the approval policy (aliases: `/perm`) |
| `/new` | start a fresh session |
| `/clear` | clear the scrollback |
| `/help`, `/exit` | help / quit (Ctrl+C also quits) |
Presets: each session is composed from one DSH **agent preset** (the same mechanism as the web app — 标准/`standard`, PTC/`code`, 极简/`minimal`, 创造/`cordis`). The default is `standard`; set a persistent default in `$DSH_HOME/settings.yaml` under `agent-presets.default`, and author your own presets in `$DSH_HOME/.agent-presets/` (a directory with `agent.cordis.yml` + optional `preset.yml`).
Tool approval prompt (inline on the same input area):
```
? [授权] pwsh: … — y 允许一次 / n 拒绝 / 回车 允许
```
Color scheme (override via env `DSH_CLI_THEME=<path-to-json>`; each value is an SGR parameter string):
```json
{ "user": "1;92", "reasoning": "2;90", "toolCall": "33",
"toolResult": "36", "toolError": "1;31", "system": "35", "status": "36" }
```
## How it works
Three layers, all plain files:
| Layer | Where | What |
| ------- | -------------------------------------------------- | ----------------------------------------------------- |
| bundle | repo root (npm package `dsh-simple-cli`) | `cordis.patch.yml` + `lib/` — the runner plugin |
| profile | `$DSH_HOME/profiles/cli` | manifest listing bundles `@deepseek-ai/dsh-base` + `dsh-simple-cli` |
| shim | npm global bin dir | `dsh-cli` → `dsh --profile cli` |
- **Bundle resolution** (`resolveBundleDir`) checks the dsh install directory first, then the profile's own `node_modules` — so dropping the bundle at `profiles/cli/node_modules/dsh-simple-cli` is enough.
- **Dependencies** — every runtime import (`@deepseek-ai/dsh-agent`, `dsh-llm`, `dsh-session`, `schemastery`, `commander`, …) resolves up the directory tree into the shared `profiles/node_modules` that ships with dsh. **No `pnpm install` needed.**
- **Runner** — `lib/index.js` creates one Agent through the core registry, then renders the `session/event` firehose live: `assistant/chunk` deltas stream reasoning / text / tool-call arguments, `tool/call` + `tool/result` draw the tool lines, and `approval/request` is answered inline on the same input area. Non-TTY stdin (pipes / CI) falls back to a plain readline loop.
- **UI** — `lib/ui.js` is a dependency-free raw-mode mini-TUI (ANSI only): scrollback + `──` separator + multi-line editor + status bar; `lib/theme.js` holds the palette.
- **Note** — `dsh` rewrites the profile's `cordis.yml` to `[]` on every launch; keep your changes in `cordis.patch.yml`.
## Repository layout
```
dsh-simple-cli/
├── README.md # this file
├── LICENSE # MIT
├── package.json # the dsh-simple-cli bundle package
├── cordis.patch.yml # bundle patch (loader patch list)
├── lib/
│ ├── index.js # cli-runner plugin: agent loop, event renderer, /model /permission
│ ├── startup.js # cli-startup plugin: command-line parsing
│ ├── ui.js # raw-mode mini-TUI: input area, streaming view, status bar
│ ├── theme.js # color palette + DSH_CLI_THEME override
│ └── refs.js # @file references: find + attach referenced file contents
├── test/
│ ├── ui.test.mjs # Tui unit tests (node test/ui.test.mjs)
│ └── refs.test.mjs # @file reference unit tests (node test/refs.test.mjs)
├── profile/ # cli profile template
│ ├── package.json
│ ├── cordis.patch.yml
│ └── pnpm-workspace.yaml
├── bin/ # dsh-cli command shims
│ ├── dsh-cli.cmd # Windows cmd
│ ├── dsh-cli.ps1 # PowerShell
│ └── dsh-cli # POSIX sh
└── install.ps1 # one-click installer
```
## Development
Edit the bundle sources in this repo, then sync the running copy:
```powershell
Copy-Item -Recurse -Force .\lib, .\package.json, .\cordis.patch.yml "$HOME\.dsh\profiles\cli\node_modules\dsh-simple-cli\"
```
## Troubleshooting
- **`dsh --profile cli` says the profile does not exist** — the profile directory needs `package.json`; run `.\install.ps1`.
- **`cordis.yml` keeps coming back as `[]`** — expected; `dsh` regenerates it. Edit `cordis.patch.yml` instead.
- **Renamed the bundle** — keep `name` in `package.json`, the plugin names in `cordis.patch.yml`, and the profile bundle list in sync.
## Roadmap
- Cross-session ↑/↓ history persistence under `$DSH_HOME`
- `/sessions` session list
- Publish to npm (`dsh-simple-cli`) so others can `dsh plugin --profile cli add dsh-simple-cli`
## License
MIT © godzeason
---
# 中文
## 这是什么
> **v0.1.2** - [Releases](https://github.com/kit-zeason/dsh-simple-CLI/releases) · [Changelog](CHANGELOG.md)
`dsh-simple-cli` 给 [DeepSeek Harness](https://www.npmjs.com/package/@deepseek-ai/dsh)(`dsh`)加了一个**纯终端交互式 profile**(启动命令为 `dsh-cli`):像 Claude Code 一样在终端里持续对话,流式输出、↑/↓ 历史、工具调用授权提示。
## 特性
- **流式输出(含推理与工具调用)**:正文、模型推理(`▸ 推理`,暗灰)与工具调用(`⛭ 名称: 参数` + `✓/✗` 结果)实时流入,与 TUI 体验一致。**思考时推理折叠为单行、实时显示最新一行**(网页端风格),渲染合并到约 30fps,快速思考不会导致终端闪屏
- **`@` 文件选择器**:输入 `@` 即可浏览工作目录(继续输入过滤、↑/↓ 选择、**Enter** 进入目录或选中文件、**Esc** 关闭);选中后插入 `@相对路径`,提交时每个被引用的文本文件会作为附件注入消息(单个上限 64KB,超出截断并注明;二进制文件只保留路径引用)
- **`──` 输入区**:分隔线框住输入框;**Enter 发送**,**Shift+Enter / Ctrl+Enter 换行**(支持多行消息;需要终端支持 kitty 键盘协议——Windows Terminal 1.19+、kitty、WezTerm、iTerm2、Konsole、foot 等——否则可用 **Ctrl+J** 或 **Alt+Enter** 兜底换行),↑/↓ 翻历史或跨行移动
- **状态栏**:输入区下方实时显示当前模型与权限,并常驻切换命令提示
- **多轮持续对话**:同一 Agent 会话保持上下文连续
- **工具授权**:agent 请求调用工具时("ask" 策略)在同一输入区内联应答 `y`/`n`/回车
- **命令**:`/new` 新会话 · `/model [provider/]<model>` 切换模型(裸 `/model` 弹出可过滤的模型选择框) · `/preset [id]` 切换预设(裸 `/preset` 弹出可过滤的预设选择框) · `/permission ask|never` 切换权限 · `/clear` · `/exit`;支持可选首条消息参数
- **默认配色**:各角色分色(用户绿、推理灰、工具黄/青…);可用 `DSH_CLI_THEME` 环境变量覆盖
## 安装
### 一键安装(Windows / Unix,PowerShell)
```powershell
git clone https://github.com/kit-zeason/dsh-simple-CLI.git
cd dsh-simple-CLI
.\install.ps1 # 安装到 $DSH_HOME/profiles/cli,注册 dsh-cli 命令并自检
.\install.ps1 -Force # 覆盖已有 profile 配置文件
```
脚本会:创建 profile 目录 → 复制 bundle → 写 profile 清单 → 安装全局 `dsh-cli` shim → 验证 `dsh --profile cli --help`。
不想 clone?可直接下载安装脚本并指定版本安装:
```powershell
Invoke-WebRequest -Uri https://raw.githubusercontent.com/kit-zeason/dsh-simple-CLI/main/install.ps1 -OutFile install.ps1
.\install.ps1 -Version 0.1.0 # 下载 release v0.1.0 并安装
.\install.ps1 -Version 0.1.0 -Force # 重新安装,覆盖已有 profile 配置
```
`-Version` 接受 `0.1.0` 或 `v0.1.0`;不带该参数时安装脚本所在目录的代码(如 clone 的 `main`)。
### 手动安装(任意平台)
```powershell
$profiles = Join-Path $env:USERPROFILE '.dsh\profiles'
New-Item -ItemType Directory -Force -Path "$profiles\cli\node_modules\dsh-simple-cli\lib"
Copy-Item package.json, cordis.patch.yml "$profiles\cli\node_modules\dsh-simple-cli\"
Copy-Item lib\index.js, lib\startup.js "$profiles\cli\node_modules\dsh-simple-cli\lib\"
Copy-Item profile\package.json, profile\cordis.patch.yml, profile\pnpm-workspace.yaml "$profiles\cli\"
```
## 使用
```powershell
dsh-cli # 开始交互对话
dsh --profile cli # 等价
dsh-cli "run the tests" # 先发一条任务,之后继续对话
```
界面示意:
```
┌──────────────────────────────────────────────┐
│ dsh-cli: DeepSeek Harness 交互模式(/help …) │
│ ▸ 推理 let me check the repo layout… │ ← 暗灰,流式
│ ⛭ pwsh: Get-ChildItem -Recurse . │ ← 黄色
│ ✓ Directory: … │ ← 青色
│ 这里是最新的文件清单:… │ ← 流式正文
│──────────────────────────────────────────────│
│ > 输入你的消息… (Shift+Enter 换行, Enter 发送) │ ← 输入区
│ 模型: deepseek/deepseek-v4-flash · 权限: ask … │ ← 状态栏
└──────────────────────────────────────────────┘
```
命令一览:
| 命令 | 作用 |
| --- | --- |
| `/model` | 弹出模型选择框(浏览或输入关键字过滤后选中) |
| `/model <name>` | 切换模型(保持 provider),如 `/model deepseek-v4-flash` |
| `/model <provider>/<name>` | 同时切换 provider 与模型 |
| `/preset` | 弹出预设选择框(标准 / PTC / 极简 / 创造 + 自定义) |
| `/preset <id>` | 切换预设(空白会话立即生效,否则 `/new` 后生效) |
| `/permission ask` / `never` | 切换权限策略(别名 `/perm`) |
| `/new` | 开启新会话 |
| `/clear` | 清空回滚区 |
| `/help`、`/exit` | 帮助 / 退出(Ctrl+C 也可退出) |
预设:每个会话由一个 DSH **agent preset**(与网页端同一套机制——标准/`standard`、PTC/`code`、极简/`minimal`、创造/`cordis`)组装而成。默认是 `standard`;可在 `$DSH_HOME/settings.yaml` 的 `agent-presets.default` 设置持久默认值,自定义 preset 放在 `$DSH_HOME/.agent-presets/`(一个含 `agent.cordis.yml` 与可选 `preset.yml` 的目录)。
工具授权提示(同一输入区内联):
```
? [授权] pwsh: … — y 允许一次 / n 拒绝 / 回车 允许
```
配色覆盖(`DSH_CLI_THEME=<json文件路径>`,值为 SGR 参数串):
```json
{ "user": "1;92", "reasoning": "2;90", "toolCall": "33",
"toolResult": "36", "toolError": "1;31", "system": "35", "status": "36" }
```
## 工作原理
三层结构,全是普通文件:
| 层 | 位置 | 内容 |
| --- | --- | --- |
| bundle | 仓库根(npm 包 `dsh-simple-cli`) | `cordis.patch.yml` + `lib/` 运行器插件 |
| profile | `$DSH_HOME/profiles/cli` | 清单:`@deepseek-ai/dsh-base` + `dsh-simple-cli` |
| shim | npm 全局 bin | `dsh-cli` → `dsh --profile cli` |
- **bundle 解析**:先查 dsh 安装目录,再查 profile 自身 `node_modules`,因此把 bundle 放进 `profiles/cli/node_modules/dsh-simple-cli` 即可
- **依赖**:所有运行时 import 沿目录树上溯到 dsh 自带的共享 `profiles/node_modules`,**无需 pnpm install**
- **运行器**:`lib/index.js` 通过核心注册表建一个 Agent,把 `session/event` 事件流实时渲染:`assistant/chunk` 增量流式显示推理/正文/工具参数,`tool/call` + `tool/result` 绘制工具行,`approval/request` 在同一输入区内联应答;非 TTY(管道/CI)自动回退为普通 readline 循环
- **UI**:`lib/ui.js` 是无依赖的 raw 模式迷你 TUI(纯 ANSI):回滚区 + `──` 分隔线 + 多行编辑器 + 状态栏;`lib/theme.js` 提供配色
- **注意**:`dsh` 每次启动会把 profile 的 `cordis.yml` 重写为 `[]`,改动请写在 `cordis.patch.yml`
## 仓库结构
```
dsh-simple-cli/
├── README.md # 本文件(中英双语)
├── LICENSE # MIT
├── package.json # dsh-simple-cli bundle 包
├── cordis.patch.yml # bundle patch(loader patch 列表)
├── lib/
│ ├── index.js # cli-runner 插件:agent 循环 / 事件渲染 / /model /permission
│ ├── startup.js # cli-startup 插件:命令行解析
│ ├── ui.js # raw 模式迷你 TUI:输入区 / 流式视图 / 状态栏
│ ├── theme.js # 配色 + DSH_CLI_THEME 覆盖
│ └── refs.js # @file 引用:查找并附加引用文件内容
├── test/
│ ├── ui.test.mjs # Tui 单元测试(node test/ui.test.mjs)
│ └── refs.test.mjs # @file 引用单元测试(node test/refs.test.mjs)
├── profile/ # cli profile 模板
│ ├── package.json
│ ├── cordis.patch.yml
│ └── pnpm-workspace.yaml
├── bin/ # dsh-cli 命令 shim
│ ├── dsh-cli.cmd # Windows cmd
│ ├── dsh-cli.ps1 # PowerShell
│ └── dsh-cli # POSIX sh
└── install.ps1 # 一键安装脚本
```
## 开发
改完本仓库的 bundle 源码后,同步运行副本:
```powershell
Copy-Item -Recurse -Force .\lib, .\package.json, .\cordis.patch.yml "$HOME\.dsh\profiles\cli\node_modules\dsh-simple-cli\"
```
## 已知的坑
- `dsh --profile cli` 报 profile 不存在 → 需先建 `profiles/cli/package.json`,跑一遍 `.\install.ps1`
- `cordis.yml` 总被重置为 `[]` → 正常现象,改 `cordis.patch.yml`
- 改 bundle `name` → 同步 `cordis.patch.yml` 里的插件名和 profile 的 bundles 列表
## 路线图
- 跨会话 ↑/↓ 历史持久化(存 `$DSH_HOME`)
- `/sessions` 历史会话列表
- 发布 npm(`dsh-simple-cli`),他人可 `dsh plugin --profile cli add dsh-simple-cli`
## License
MIT © godzeason
Install
dsh plugin --profile web add github:kit-zeason/dsh-simple-CLI
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-simple-cli from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.