dsh-oc-tui 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,216 +1,355 @@
1
- # Deepseek Harness opencode-like TUI Plugin
1
+ # dsh-oc-tui
2
2
 
3
- An opencode-inspired **terminal UI (TUI)** for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), shipped as a dsh profile app plugin. It boots a chat client inside the dsh process: it creates/resumes agents through `ctx.agents`, renders the durable `session/event` stream (user messages, streaming assistant tokens, tool cards, todo lists), routes human input back via `agent.followup()`, and answers `approval/request` prompts inline.
3
+ **A terminal UI for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)** an opencode-inspired chat client that boots inside the `dsh` process as a profile app plugin.
4
4
 
5
- The UI follows a DeepSeek blue-white dark design language (brand blue `#4D6BFE` accents on a blue-tinted canvas, with a blue→white gradient on the DeepSeek Harness title): a responsive session rail, markdown transcript and tool activity, a bordered multiline composer with slash-command suggestions, and a telemetry footer for session tokens, average time to first token (TTFT), decode throughput, KV-cache hit rate, and a **context meter** showing the current context length over the context-length limit. Thinking and running tools (read/write/...) show flowing spinner animations, the status row carries a flowing wave, and the composer border becomes a flowing gold marching-ants frame (dashes chasing clockwise around the box) while the agent is working.
5
+ [![npm latest](https://img.shields.io/npm/v/dsh-oc-tui?label=npm&color=BF392B)](https://www.npmjs.com/package/dsh-oc-tui)
6
+ [![awesome-dsh-plugin](https://img.shields.io/badge/awesome--dsh--plugin-marketplace-BF392B)](https://awesome-dsh-plugin.com/zh/p/rayafriandion/dsh-oc-tui/)
7
+ [![License: LGPL-3.0-or-later](https://img.shields.io/badge/license-LGPL--3.0--or--later-blue.svg)](LICENSE)
8
+ [![Node](https://img.shields.io/badge/node-%3E%3D22-339933.svg)](https://nodejs.org)
6
9
 
7
- ![max thinking effect](docs/max-thinking.gif)
10
+ `dsh-oc-tui` renders the harness's durable event stream in your terminal — streaming replies, tool cards, todo lists, thinking blocks — and routes what you type back into the agent. Model routing, tool execution, approvals, commands, durable sessions, and credentials stay owned by DSH; this package owns terminal input and presentation.
8
11
 
9
- 中文用户手册见 [docs/用户手册.md](docs/用户手册.md)
12
+ Published on **npm** as [`dsh-oc-tui`](https://www.npmjs.com/package/dsh-oc-tui) and listed in the [**awesome-dsh-plugin**](https://awesome-dsh-plugin.com/zh/p/rayafriandion/dsh-oc-tui/) marketplace.
13
+
14
+ > 中文文档:[docs/用户手册.md](docs/用户手册.md)
15
+
16
+ ## Table of contents
17
+
18
+ - [Features](#features)
19
+ - [Requirements](#requirements)
20
+ - [Install](#install)
21
+ - [Quick start](#quick-start)
22
+ - [Usage](#usage)
23
+ - [Keybindings](#keybindings)
24
+ - [Slash commands](#slash-commands)
25
+ - [Interactive prompts](#interactive-prompts)
26
+ - [Thinking intensity](#thinking-intensity)
27
+ - [Session stats and the context meter](#session-stats-and-the-context-meter)
28
+ - [Settings](#settings)
29
+ - [In-app updates](#in-app-updates)
30
+ - [How it works](#how-it-works)
31
+ - [Development](#development)
32
+ - [Troubleshooting](#troubleshooting)
33
+ - [Known limitations](#known-limitations)
34
+ - [Layout](#layout)
35
+ - [License](#license)
36
+
37
+ ## Features
38
+
39
+ | | |
40
+ | --- | --- |
41
+ | **Durable sessions** | Create, resume, list, and delete sessions; the transcript is rebuilt from the persisted event log, so a resumed session looks exactly like the one you left. |
42
+ | **Live streaming** | Assistant text and reasoning stream token by token; thinking renders in its own collapsible box that stays collapsed while streaming. |
43
+ | **Tool activity** | Tool cards with a one-line summary (`read src/app.ts`, `run npm test`), flowing spinners while running, and markdown-rendered results. |
44
+ | **Interactive questions** | The model can pause and ask you — option lists, multi-select, free text, and a scrollable plan review — all inline in the terminal. See [Interactive prompts](#interactive-prompts). |
45
+ | **Inline approvals** | `approval/request` prompts are answered with `y` / `n` without leaving the UI. |
46
+ | **Session stats** | One stats strip above the composer — turns/steps, LLM and tool wall time, average TTFT, decode throughput, cache-hit rate, and billed input/output tokens — folded from durable events. See [Session stats and the context meter](#session-stats-and-the-context-meter). |
47
+ | **Stats window** | Click the strip or the context meter, or type `/stats`, for the full session-statistics and token-usage breakdown. |
48
+ | **Context meter** | Live context occupancy (`ctx ▓▓░░ 32K/128K 25%`), with the system/tools/messages composition in the same window. |
49
+ | **Thinking intensity** | `Tab` cycles the current model's real reasoning levels; `Ctrl+E` opens a slider. The level is applied per request and persisted. |
50
+ | **Shared settings** | The same host settings namespaces the Web UI uses — general, sessions, per-provider model configuration, credentials — persisted to `$DSH_HOME/settings.yaml`. |
51
+ | **In-app updates** | Detect and switch versions of `@deepseek-ai/dsh` and `dsh-oc-tui` from inside the TUI, with Windows-safe deferred installs. |
52
+ | **Zero-dependency terminal engine** | Raw mode, alternate screen, a diffing cell buffer, truecolor ANSI, CJK-aware widths, SGR + legacy X10 mouse decoding, and IME caret anchoring. |
10
53
 
11
54
  ## Requirements
12
55
 
13
- - Node.js >= 22, an installed `@deepseek-ai/dsh` CLI, and an interactive terminal (Windows Terminal / ConPTY, iTerm2, GNOME Terminal, ...).
14
- - `pnpm` on PATH for the one-time plugin install (`dsh plugin` forwards to pnpm).
15
- - A configured model route: the profile reuses `$DSH_HOME/settings.yaml` (`llm-pi-ai` providers or `llm-deepseek`) and `$DSH_HOME/.credentials.yaml` — the same setup the Web GUI uses.
56
+ | | |
57
+ | --- | --- |
58
+ | Node.js | >= 22 |
59
+ | dsh CLI | `@deepseek-ai/dsh` — e.g. `npm install -g @deepseek-ai/dsh` |
60
+ | pnpm | on `PATH`; `dsh plugin` forwards to it |
61
+ | Terminal | an interactive terminal (Windows Terminal / ConPTY, iTerm2, GNOME Terminal, …) |
62
+ | Model route | a usable route in `$DSH_HOME/settings.yaml` + `$DSH_HOME/.credentials.yaml` (the same setup the Web GUI uses) |
16
63
 
17
- ## Quick start
64
+ ```sh
65
+ dsh --version
66
+ pnpm --version
67
+ ```
68
+
69
+ **Compatibility.** Verified against dsh `0.1.2-rc.1` (and `0.1.1-rc.2`). DSH renamed parts of the session API in 0.1.2 — `Session.events` became `snapshotEvents()` — and this plugin reads whichever accessor the host provides, so one build serves both lines.
70
+
71
+ ## Install
72
+
73
+ ### From npm
74
+
75
+ The package is published on npm as [`dsh-oc-tui`](https://www.npmjs.com/package/dsh-oc-tui). Install it into the `tui` profile:
18
76
 
19
77
  ```sh
20
- # 1. One-time setup: create the tui profile and install this plugin into it.
21
- dsh plugin --profile tui add dsh-oc-tui
78
+ dsh plugin --profile tui add -w dsh-oc-tui
79
+ ```
80
+
81
+ Or install the launcher globally — that puts the `dsh-oc-tui` command on `PATH`, which then boots `dsh --profile tui`:
22
82
 
23
- # 2. Launch.
24
- dsh --profile tui
83
+ ```sh
84
+ npm install -g dsh-oc-tui
25
85
  ```
26
86
 
27
- The first command initializes `$DSH_HOME/profiles/tui` with `@deepseek-ai/dsh-base`, installs this package with pnpm, and appends it to the profile's `dsh.profile.bundles` because the package declares `dsh.bundle`. Nothing else needs editing.
87
+ ### Version channels
28
88
 
29
- Verify the layer without booting:
89
+ The **npm package** and the **[awesome-dsh-plugin](https://awesome-dsh-plugin.com/zh/p/rayafriandion/dsh-oc-tui/) marketplace entry** both ship **stable releases only** — pre-releases are never published to either. `npm install` therefore gives you the latest stable version, not a release candidate.
90
+
91
+ This README describes the current source tree, which can be ahead of the published release — a feature documented here is only guaranteed to exist in a stable build once that version is on npm.
92
+
93
+ To run a pre-release, or unreleased work from this repository, install it explicitly from source:
30
94
 
31
95
  ```sh
32
- dsh --profile tui --dump-config
96
+ npm pack # -> dsh-oc-tui-<version>.tgz
97
+ dsh plugin --profile tui add -w ./dsh-oc-tui-<version>.tgz
33
98
  ```
34
99
 
35
- The dump shows the `dsh-oc-tui` bundle layer after `@deepseek-ai/dsh-base`.
100
+ ### One-command installers
36
101
 
37
- ## Launch
102
+ The repository ships installers that check Node >= 22, make sure `pnpm` exists, install the plugin into the `tui` profile, and can also add the `dsh-oc-tui` launcher globally.
103
+
104
+ ```sh
105
+ # Linux / macOS
106
+ curl -fsSL https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.sh | bash
107
+ ```
108
+
109
+ ```powershell
110
+ # Windows (PowerShell)
111
+ powershell -ExecutionPolicy Bypass -Command "iwr https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.ps1 -OutFile install.ps1; & .\install.ps1"
112
+ ```
113
+
114
+ Run `./install.sh` / `.\install.ps1` from a checkout instead, and add `--launcher` / `-Launcher` to also put the `dsh-oc-tui` command on `PATH`. Other flags: `--local` (`-Local`) installs the current checkout, `--source <spec>` (`-Source <spec>`) uses a custom source, `--profile <name>` (`-Profile <name>`) targets another profile.
115
+
116
+ ### From a checkout or tarball
117
+
118
+ ```sh
119
+ npm pack # -> dsh-oc-tui-<version>.tgz
120
+ dsh plugin --profile tui add -w ./dsh-oc-tui-<version>.tgz
121
+ ```
122
+
123
+ `dsh plugin` anchors relative paths to the directory you invoke it from before forwarding to pnpm.
124
+
125
+ ### Why `-w`
126
+
127
+ The profile directory declares itself a pnpm workspace root (`pnpm-workspace.yaml` → `packages: [.]`), so pnpm refuses a bare `add` with `ERR_PNPM_ADDING_TO_ROOT`. `-w` makes the dependency land in the profile's own manifest — which is exactly what it is. `dsh plugin` then reconciles `dsh.profile.bundles` against what is installed.
128
+
129
+ ### What the install does
130
+
131
+ 1. `dsh plugin` initializes `$DSH_HOME/profiles/tui` on first use (`@deepseek-ai/dsh-base` plus an empty user patch layer).
132
+ 2. pnpm installs `dsh-oc-tui` into the profile's `node_modules`.
133
+ 3. Because the package declares `dsh.bundle.patch`, dsh appends `dsh-oc-tui` to `dsh.profile.bundles`.
134
+ 4. `dsh --profile tui` composes the base layer, this bundle's rows, and your own patch — no manual editing required.
135
+
136
+ Verify without booting:
137
+
138
+ ```sh
139
+ dsh --profile tui --dump-config
140
+ ```
38
141
 
39
- ### `dsh --profile tui`
142
+ The dump shows a `# == dsh-oc-tui` layer containing `tui-startup`, `tui-app`, the `agent-presets` roster row, and the `tool-ask-user` row.
40
143
 
41
- The canonical launch is the dsh launcher itself:
144
+ ## Quick start
42
145
 
43
146
  ```sh
44
- dsh --profile tui # open the title screen; first message creates a session
45
- dsh --profile tui --resume <sessionId> # resume a persisted session
46
- dsh --profile tui --model <modelId> # default model for new sessions
47
- dsh --profile tui --provider <route> # default provider route
48
- dsh --profile tui --no-sidebar # start without the sidebar
49
- dsh --profile tui --help # the TUI's own flags
147
+ dsh --profile tui # title screen; your first message creates a session
148
+ dsh --profile tui --resume <sessionId> # resume a persisted session
149
+ dsh --profile tui --model <modelId> # default model for new sessions
150
+ dsh --profile tui --provider <route> # default provider route
151
+ dsh --profile tui --no-sidebar # start without the session rail
152
+ dsh --profile tui --help # the TUI's own flags
153
+ ```
154
+
155
+ The stock launcher hardcodes only `web` and `plugin` as bare subcommands, so `--profile tui` is the intended shape. Want the literal `dsh tui`? Add a shell alias:
156
+
157
+ ```powershell
158
+ function tui { dsh --profile tui @args } # PowerShell $PROFILE
50
159
  ```
51
160
 
52
- The stock dsh launcher only hardcodes `web` and `plugin` as bare subcommands, so the profile flag is the intended shape for custom surfaces. If you want the exact string `dsh tui`, add a one-line shell alias (for example `doskey tui=dsh --profile tui $*` in CMD).
161
+ ```bat
162
+ doskey tui=dsh --profile tui $* :: CMD
163
+ ```
53
164
 
54
- ### `dsh-oc-tui` convenience launcher
165
+ ### Convenience launcher
55
166
 
56
- The package also ships a `dsh-oc-tui` bin. It is equivalent to `dsh --profile tui`, but it verifies that the `tui` profile actually has the plugin installed and prints the one-time setup command when it does not:
167
+ The package also ships a `dsh-oc-tui` binary that is equivalent to `dsh --profile tui`, but checks first that the profile actually has the plugin installed and prints the one-time install command when it does not.
57
168
 
58
169
  ```sh
59
170
  dsh-oc-tui # boot the tui profile
60
- dsh-oc-tui --profile mytui # boot a profile with a different name
171
+ dsh-oc-tui --profile mytui # boot a different profile
61
172
  dsh-oc-tui --help # launcher help
173
+ dsh-oc-tui --version # launcher version
62
174
  ```
63
175
 
64
- The launcher prefers an installed `dsh` on PATH and falls back to `npx --yes @deepseek-ai/dsh`. Install the bin globally with `npm install -g dsh-oc-tui` (or run it from a local checkout with `node bin/dsh-oc-tui.js`). Environment overrides:
176
+ It prefers the `dsh` on `PATH` and falls back to `npx --yes @deepseek-ai/dsh`. Install it with `npm install -g dsh-oc-tui`.
65
177
 
66
- | Variable | Effect |
178
+ | Environment variable | Effect |
67
179
  | --- | --- |
68
- | `DSH_TUI_PROFILE` | Default profile name when `--profile` is not given. |
180
+ | `DSH_TUI_PROFILE` | Default profile when `--profile` is absent (default `tui`). |
69
181
  | `DSH_TUI_SKIP_CHECK` | Set to `1` to skip the profile preflight (advanced installs). |
70
182
 
71
183
  ## Usage
72
184
 
73
- | Flag | Effect |
74
- | --- | --- |
75
- | `--resume <sessionId>` | Resume a persisted session by id. |
76
- | `--model <modelId>` | Default model id for new sessions. |
77
- | `--provider <provider>` | Default provider route for new sessions. |
78
- | `--sidebar` / `--no-sidebar` | Show (default) or hide the session sidebar. |
79
- | `--help` | Print the TUI's own flag help. |
80
-
81
185
  ### Keybindings
82
186
 
83
187
  | Key | Action |
84
188
  | --- | --- |
85
- | Enter | send message |
86
- | Ctrl+Enter / Shift+Enter / Alt+Enter | insert a newline |
87
- | Ctrl+C | clear a non-empty prompt, cancel a running turn, or press twice while idle to exit |
88
- | Ctrl+P | open the settings menu |
89
- | Ctrl+E | toggle the thinking slider below the input box |
90
- | Tab | cycle the thinking intensity (session page); switch the settings left menu (settings page) |
91
- | Ctrl+N | new session |
92
- | Ctrl+D | delete the focused session in Settings Manage sessions |
93
- | Ctrl+L | clear the transcript view |
94
- | Up / Down | move the caret across multi-line input; on the first/last row they step through input history |
95
- | Left / Right | move the caret left/right in the input box |
96
- | PgUp / PgDn | scroll the transcript |
97
- | Esc | close the context-meter panel / thinking slider / help, or cancel an approval prompt |
98
- | y / n | answer an inline approval prompt |
189
+ | `Enter` | Send the message. |
190
+ | `Ctrl+Enter` / `Shift+Enter` / `Alt+Enter` | Insert a newline. |
191
+ | `Ctrl+C` | Clear a non-empty prompt, cancel the running turn, or press twice while idle to exit. |
192
+ | `Ctrl+P` | Open Settings. |
193
+ | `Ctrl+E` | Toggle the thinking-intensity slider below the composer. |
194
+ | `Tab` | Session page: cycle the thinking intensity. Settings page: switch the left menu. |
195
+ | `Ctrl+N` | New session. |
196
+ | `Ctrl+D` | In Settings → Manage sessions: delete the focused session (press twice to confirm). |
197
+ | `Ctrl+L` | Clear the transcript view. |
198
+ | `Up` / `Down` | Move the caret across a multi-line prompt; on the first/last row, step through input history. |
199
+ | `Left` / `Right` | Move the caret within the input box. |
200
+ | `PgUp` / `PgDn` | Scroll the transcript. |
201
+ | `Esc` | Close the session stats window, the thinking slider, or help; cancel an approval; cancel a running turn; clear the prompt you are typing. |
202
+ | `Esc Esc` | Idle with an empty prompt: open the rewind picker. |
203
+ | `y` / `n` | Answer an inline approval prompt. |
99
204
 
100
- Mouse: the wheel scrolls the transcript (or the settings window when Settings is open — it no longer moves the settings focus). Hold the left button and drag across the transcript to select text, then press the right button to copy the selection to the clipboard.
205
+ **Mouse.** The wheel scrolls the transcript (or the Settings window while it is open). Hold the left button and drag across the transcript to select text, then press the right button to copy the selection.
101
206
 
102
- ### Commands
207
+ ### Slash commands
103
208
 
104
- `/help` `/settings` `/new` `/resume <id>` `/model <id>` `/provider <route>` `/clear` `/cancel` `/quit`
209
+ Built in: `/help` `/settings` `/new` `/resume <id>` `/model <id>` `/provider <route>` `/rewind` `/stats` `/clear` `/cancel` `/quit` (`/exit` also works).
105
210
 
106
- Harness human commands (`/compact`, `/goal`, ...) are forwarded to `ctx.commands` and run without a model turn.
211
+ **Rewind.** `Esc Esc` (or `/rewind`) lists the prompts of the live session. Restoring the **conversation** forks a new session from the events before the chosen prompt — the parent session is left untouched on disk, exactly as the harness's own `session/fork` does — and the picker lands on the most recent prompt, so `Enter` twice rewinds the last turn. `/rewind <n|last> [conversation|code|both]` runs it without the picker. The fork starts with an **empty inbox**: a cut before a turn also cuts the inbox claim that turn performed, so anything the parent had queued — including the prompt you rewound away from — is not delivered again; it stays in the parent's log, and the result line says `dropped N inherited pending input` when there was any. Restoring **files** is best-effort and fenced: it needs a git worktree (anywhere else the rewind reports `files not restored (not a git worktree)` and changes nothing), it rewrites tracked files from `HEAD` without touching the index, and it removes an untracked file only when the transcript's first recorded write to that path is at or after the rewind point. Everything it overwrites or deletes is copied to `$DSH_HOME/rewind-backups/<sessionId>/<timestamp>/` first, and the result line names that directory. Because the log stores no file contents, a tracked file returns to its last commit, not to its exact state at the rewind point.
107
212
 
108
- ## Install
213
+ Harness commands — `/compact`, `/goal`, `/plan`, … — are forwarded to `ctx.commands` and run without a model turn. They need a live session: on the title screen the TUI answers `/<name>: start a session first` instead of dropping the command silently.
109
214
 
110
- ### Automatic install script
215
+ `/stats` is the TUI's own command: it toggles the [session stats window](#session-stats-and-the-context-meter) and, like the harness commands, needs a live session.
111
216
 
112
- The repo ships one-command installers for Linux/macOS and Windows. They check Node.js >= 22, ensure `pnpm` is present, install the plugin from GitHub into the `tui` profile, and can also install the `dsh-oc-tui` launcher globally.
217
+ ### Interactive prompts
113
218
 
114
- ```sh
115
- # Linux / macOS
116
- curl -fsSL https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.sh | bash
219
+ **Approvals.** When a tool needs permission, the composer area shows `Approval · <tool> · y allow / n deny`. `y` allows once, `n` rejects, `Esc` cancels. The plugin also honours the effective permission preset, so an auto-approving preset does not prompt at all.
117
220
 
118
- # Windows (PowerShell)
119
- powershell -ExecutionPolicy Bypass -Command "iwr https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.ps1 -OutFile install.ps1; & .\install.ps1"
120
- ```
221
+ **Questions.** The model can ask you directly through the `ask_user_question` tool. The tool is declared by this bundle's `tool-ask-user` row — `dsh-base` mounts the `user-questions` service but not the tool, and a TUI session composes from the base rather than from an agent preset — and it is answered by a modal:
121
222
 
122
- From a checkout you can run `./install.sh` (Linux/macOS) or `.\install.ps1` (Windows) directly. Add `--launcher` / `-Launcher` to also put the `dsh-oc-tui` command on PATH:
223
+ | Key | Action |
224
+ | --- | --- |
225
+ | `Up` / `Down` | Move between options (wrapping). |
226
+ | `Space` | Toggle the highlighted option (multi-select) or select it (single choice). |
227
+ | `Enter` | Continue: a single choice is selected and advances; on the free-text row it starts editing; in a multi-select it confirms the toggled set. |
228
+ | any printable key | Jump into the free-text row and start typing. |
229
+ | `PgUp` / `PgDn`, wheel | Scroll a long plan or detail pane. |
230
+ | `Esc` | Defer: decline to answer here (`Esc` while editing returns to the options). |
231
+ | `Ctrl+C` | Still cancels the running turn; the pending question is withdrawn. |
123
232
 
124
- ```sh
125
- ./install.sh --launcher # Linux/macOS
126
- .\install.ps1 -Launcher # Windows
127
- ```
233
+ Questions are staged one at a time, exactly as the Web UI composer stages them, and the answer encoding is identical: a free-text answer replaces the selection for a single-select question and accompanies it for a multi-select one.
128
234
 
129
- Other options: `--local` (`-Local`) installs the current checkout instead of GitHub, `--source <spec>` (`-Source <spec>`) uses a custom source (e.g. `dsh-oc-tui` once published to npm, or a tarball path), and `--profile <name>` (`-Profile <name>`) targets a non-default profile.
235
+ A question carrying the `plan-review` intent what `exit_plan_mode` sends renders the plan markdown in a scrollable pane above `Approve` / `Keep planning`. Answering `Approve` exits plan mode and the model continues; anything else keeps planning.
130
236
 
131
- ### From the npm registry
237
+ Deferring is deliberate, not a cancel: with no other answerer the tool reports `no user-questions answerer accepted the request`, which cannot be mistaken for a human choice.
132
238
 
133
- ```sh
134
- dsh plugin --profile tui add dsh-oc-tui
135
- ```
239
+ ### Thinking intensity
136
240
 
137
- ### From a local checkout or tarball
241
+ The effective level sits on the composer's top-right border as the bare level name, diagonally opposite the `provider · model` label.
138
242
 
139
- ```sh
140
- dsh plugin --profile tui add ./dsh-oc-tui
141
- # or a packed tarball:
142
- dsh plugin --profile tui add ./dsh-oc-tui-0.1.0.tgz
143
- ```
243
+ - `Tab` on the session page cycles the levels of the **current model** (wrapping strongest → weakest); `Shift+Tab` steps backwards.
244
+ - `Ctrl+E` opens a slider below the composer: `Tab` or `←`/`→` adjust and persist, `Esc` or `Ctrl+E` close it.
245
+ - Levels come from the provider adapter (`ctx.llm.resolveModelInfo`), so a boolean-thinking model shows exactly its two ends, DeepSeek's `Off`/`High`/`Max` shows those three, and a full-range model shows every advertised level — never a blanket `none → max` scale.
144
246
 
145
- `dsh plugin` anchors relative paths to your invoking directory before forwarding to pnpm.
247
+ The choice is applied to the session's requests through the `agent/request` waterfall and stored in `agent-default-model.reasoningEffort`.
146
248
 
147
- ### From GitHub
249
+ ### Session stats and the context meter
148
250
 
149
- ```sh
150
- dsh plugin --profile tui add github:you/dsh-oc-tui
251
+ The row above the composer is the **session stats strip**, the TUI's counterpart of the web chat's stats line: the same figures in the same order, separated by `│`:
252
+
253
+ ```
254
+ ▤ 1 turn · 2 steps│LLM 1.3s · tools 1.2s│TTFT avg 400ms · 20.0 tok/s│cache 55%│in 110 · out 30
151
255
  ```
152
256
 
153
- This package ships plain JavaScript, so a git install needs no build step. pnpm 10 may still require allowlisting the git dependency's package key under `allowBuilds` in the profile's `pnpm-workspace.yaml` if a build step is ever added.
257
+ - `turns` / `steps` count **closed steps** (`step/end`), so failed, cancelled, and max-tokens steps count too. `LLM` is `step/start` assembled reply; `tools` pairs `tool/call` `tool/result`.
258
+ - `TTFT avg` is the per-step average time to first token; `tok/s` is decode throughput (first token → assembled reply over the reported output tokens).
259
+ - `cache` is the prompt-side cache-hit share (cache reads over all billed input); `in` / `out` are the session's billed input and output tokens.
260
+ - A narrow terminal drops trailing groups **whole** and marks the elision with `│…` rather than cutting a figure in half; the window always carries the complete set.
261
+ - A session with no closed step and no billed tokens hides the strip entirely and gives the row back to the transcript.
154
262
 
155
- ### What the install does
263
+ The whole strip is a click target. Clicking it — or the context meter at the right end of the status row (`ctx ▓▓░░ 32K/128K 25%`), or typing `/stats` — opens the **session stats window**; click again, click elsewhere, or press `Esc` to close. The window breaks the same line into labelled rows (`usage` / `duration` / `speed` / `tokens` / `cache`) and adds the context-occupancy reading with its heuristic composition — system prompt, tools, and messages — matching the Web UI's ContextMeter dialog.
156
264
 
157
- 1. `dsh plugin` initializes `$DSH_HOME/profiles/tui` on first use (`@deepseek-ai/dsh-base` plus an empty user patch layer).
158
- 2. pnpm installs `dsh-oc-tui` into the profile's `node_modules`.
159
- 3. `dsh` appends `dsh-oc-tui` to `dsh.profile.bundles` because the package declares `dsh.bundle.patch`; the bundle patch inserts the `tui-startup` and `tui-app` rows.
160
- 4. `dsh --profile tui` composes `@deepseek-ai/dsh-base` + `dsh-oc-tui` and boots the UI.
265
+ Figures come from the same sources as the Web UI, projection-first with the plugin's own fold as the fallback: `tokenUsage`, `contextPressure`, and `contextBreakdown` are mounted by `dsh-base`'s token-meter row, while `sessionStats` is mounted only by the web app bundle — so the TUI folds the durable `step` / `chunk` / `message` / `tool` events by the same rules. A missing projection falls back for that figure alone, and a figure nobody can supply stays hidden instead of printing a zero.
161
266
 
162
- To remove:
267
+ ### Settings
163
268
 
164
- ```sh
165
- dsh plugin --profile tui remove dsh-oc-tui
166
- ```
269
+ `Ctrl+P` opens a settings menu over the same host settings namespaces as the Web UI, persisted through `ctx.settings` to `$DSH_HOME/settings.yaml`. A left menu splits it into three tabs (`Tab` or click to switch):
270
+
271
+ - **Main** — General (busy-Enter behaviour, default agent preset, permission preset), Sessions (new session, manage sessions), System (provider API hints, update-manager shortcut, settings file path).
272
+ - **Model** — the default provider/model/reasoning choice, then one group per provider holding its URL, API key, and model list. Pressing `Enter` on **Models** fetches the provider's advertised catalog (`ctx.llm.discoverModels`) and opens a checkbox window; pressing `Enter` on a listed model makes it the default route.
273
+ - **Update** — see [In-app updates](#in-app-updates).
274
+
275
+ Only providers you actually added (present in your user settings layer) are listed; a provider that was never added stays hidden. The default agent preset comes from the roster the profile mounts (the shipped presets plus any you authored under `$DSH_HOME/.agent-presets`) — note that a TUI session composes process-wide from the base, so the stored default applies where a session is created from a preset. Web-UI-only options (`ui-theme` appearance, `locale`) are not shown because they have no effect in the TUI.
167
276
 
168
- ## Settings
277
+ ### In-app updates
169
278
 
170
- The settings menu projects the same Host settings namespaces used by the WebUI and persists changes through `ctx.settings` to `$DSH_HOME/settings.yaml`. A left menu bar splits it into three tabs that `Tab` (or a click) switches: **Main** keeps the general settings — General (Busy Enter behavior, default agent and permission presets), Sessions (new session, session management), and System (including an `Update manager` shortcut to the Update tab); **Model** merges the former provider settings and model settings into one tree: the default provider/model/reasoning choice, then one block per provider holding its Provider URL, Provider API key, and Models; **Update** carries the in-app update manager (see below). Only providers you have actually added (present in your user settings layer) are listed — preset providers that were never added stay hidden. Under Models the saved selection is listed one row per model (Enter makes a listed model the default route); pressing Enter on the Models row auto-fetches the provider's advertised catalog (`ctx.llm.discoverModels` — the installed catalog for a known route, or an endpoint interrogation for a custom route) and opens a selection window of checkboxes where you choose which models to keep. The default agent preset is chosen from the roster the profile mounts (`agent-presets`): the shipped presets plus any you authored under `$DSH_HOME/.agent-presets`; TUI sessions keep composing process-wide from the base, so the stored default only applies where a session is created from a preset. WebUI-only options (`ui-theme` Appearance and `locale` Language) are intentionally not shown because they have no effect inside the TUI. Choice items open their option list with Enter - there is no inline left/right value cycling.
279
+ `Ctrl+P Update` shows the installed versions of `@deepseek-ai/dsh` and `dsh-oc-tui`, the latest npm dist-tag, and a status line that only ever targets **stable** releases:
171
280
 
172
- **Thinking intensity.** The effective level stays visible on the composer's top-right border as the bare level name (no "effort" caption), diagonally opposite the `provider · model` label. Press `Tab` on the session page to cycle the intensity through the current model's levels (wrapping strongest weakest); `Shift+Tab` steps backwards. Press `Ctrl+E` to open a slider below the input box; `Tab` or `←`/`→` move it and persist the choice, `Esc` or `Ctrl+E` closes it. The slider is driven by the current model's actual selectable levels reported by the provider adapter (`ctx.llm.resolveModelInfo`), so a boolean-thinking model shows exactly its two ends, a full-range model shows every level it advertises, and a partial model (for example DeepSeek's `Off`/`High`/`Max`) shows only those never a blanket `none max` scale. At the strongest available level, the track gradient and bright sweep move left to right, the empty track shimmers, and the top-right label receives a flowing gradient with a pulsing text arrow. The Settings → Model → Reasoning entry stays available as a list menu over the same levels. The chosen level is applied to the session's requests through the `agent/request` waterfall and stored in `agent-default-model.reasoningEffort`.
281
+ - `Update availablex.y.z` — a newer stable release exists.
282
+ - `Up to date` — nothing to do.
283
+ - `No stable release — pick from Versions` — the registry has no stable release yet; pick one manually.
284
+ - `Install damaged — reinstall below` — the global dsh tree is in a mixed old/new state; reinstall it.
173
285
 
174
- **Context meter.** The status row carries a live context-occupancy bar (`ctx ▓▓░░ 32K/128K 25%`) fed by the token-meter `contextPressure` projection, the same source as the Web UI's composer ring. It shows the current context length over the context-length limit once the provider reports both; the fill shifts toward the warning/error palette as occupancy climbs. Click the meter (or press `Esc` to close) to open a breakdown panel with the occupancy reading and the heuristic composition shares — system prompt, tools, and messages matching the Web UI's ContextMeter dialog. The meter stays hidden when the profile lacks the token-meter projections.
286
+ `Enter` on a package's **Versions** row opens the full registry list (newest first, `[latest]`/`[next]`/other tags and `(installed)` colour-coded) where you can pick any version including pre-releases for a y/n-confirmed install through `npm`/`dsh plugin`. `Check now` re-reads the registry; `Startup check` toggles the silent boot-time stable-release check. Installs run in the background, never block the UI, and need a restart to apply.
175
287
 
176
- **Update manager.** `Ctrl+P → Update` shows the installed versions of `@deepseek-ai/dsh` and `dsh-oc-tui`, their latest npm dist-tag, and a status line that only ever targets **stable** releases (pre-releases such as `0.1.1-rc.2` never prompt as an update — but the factual `Latest (npm tag)` row still reports them). Enter on a package's `Versions` row opens the full registry version list (newest first, `[latest]`/`[next]`/other tags plus `(installed)` color-coded) where you pick any version — including pre-releases — to switch to; a y/n confirm shows the exact target, and the install runs through `npm`/`dsh plugin` so your configured registry and mirror are respected. Installs complete in the background with a toast and require a **restart to apply**. `Check now` re-reads the registry, and `Startup check` toggles the silent boot-time check that toasts stable updates (default `on`). Checks and installs never block the UI; a spinner runs in the page subtitle.
288
+ <details>
289
+ <summary><strong>Windows: why dsh installs are deferred to exit</strong></summary>
177
290
 
178
- On Windows, updating dsh while any dsh process runs can *silently corrupt* the global install npm replaces the directory while the running process holds memory-mapped native DLLs, and still exits 0; the hybrid old/new tree then fails to boot. The updater guards this in three layers: **dsh installs are deferred to TUI exit time** (a detached helper waits for the TUI to close, runs the install, and records the outcome in `$DSH_HOME/tui-dsh-install.json`, which the Update page verifies on the next open), **the on-disk version is re-read and compared** after every direct install so a silent corruption surfaces as an `install corrupt` toast with repair instructions, and the dsh Status row flags an already-damaged install as `Install damaged — reinstall below`. On macOS/Linux there is no DLL lock, but the install is refused while other dsh processes are running.
291
+ On Windows, updating dsh while any dsh process runs can *silently corrupt* the global install: npm replaces the directory while the running process holds memory-mapped native DLLs, still exits 0, and the resulting old/new hybrid tree fails to boot. The updater guards this in three layers:
179
292
 
180
- ## Plugin model
293
+ 1. **dsh installs are deferred to TUI exit** — a detached helper waits for the TUI to close, runs the install, and records the outcome in `$DSH_HOME/tui-dsh-install.json`, which the Update page verifies on the next visit.
294
+ 2. **The on-disk version is compared** against the requested target after every direct install, so a silent corruption surfaces as an `install corrupt` toast with repair instructions.
295
+ 3. **An already-damaged install is flagged** in the Status row rather than reported as a bogus success.
181
296
 
182
- This package is a DeepSeek Harness Cordis plugin, not a standalone agent runtime. The optional `dsh-oc-tui` binary only launches `dsh --profile tui`; DSH continues to own model routing, agent execution, tools, approvals, commands, durable sessions, and credentials. The plugin owns terminal input and presentation.
297
+ macOS/Linux have no DLL lock, but an install is refused while other dsh processes are running.
298
+ </details>
183
299
 
184
300
  ## How it works
185
301
 
186
302
  - The plugin is a Cordis function plugin loaded by the `tui` profile. `lib/startup.js` parses the app's flags and provides the `tuiStartup` service; `lib/index.js` owns the UI loop.
187
- - `lib/term.js` is a zero-dependency terminal engine: raw mode, alternate screen, a diffing cell buffer, and a key decoder (truecolor ANSI, CJK-aware widths). It parks the (hidden) terminal cursor at the input caret so the OS IME anchors its composition window inside the composer, and it understands both SGR and legacy X10 mouse encodings so wheel/click bytes can never leak into the input text.
188
- - `lib/ui.js` is the responsive view model + renderer (DeepSeek blue-white theme, session rail, transcript, multiline composer, command suggestions, and telemetry footer). Rendered transcript lines are cached per block, only the visible window is materialised each frame, streaming paints are coalesced, and the live block is re-rendered on a short throttle — so render cost stays bounded and output speed does not degrade as the history grows. `thinking` content is shown inside a gray-emphasised box that stays collapsed while streaming, collapses by default once finished, and toggles on click; thinking and running tools animate with flowing spinners.
189
- - `lib/metrics.js` folds durable step/chunk/message events into session token, average TTFT, decode throughput, and disjoint-token cache-hit metrics.
303
+ - `lib/term.js` is a zero-dependency terminal engine: raw mode, alternate screen, a diffing cell buffer, and a key decoder (truecolor ANSI, CJK-aware widths). It parks the hidden terminal cursor at the input caret so the OS IME anchors its composition window inside the composer, and it understands both SGR and legacy X10 mouse encodings so wheel and click bytes can never leak into the input text.
304
+ - `lib/ui.js` is the responsive view model and renderer (DeepSeek blue-white theme, session rail, transcript, multiline composer, command suggestions, telemetry footer). Transcript lines are cached per block, only the visible window is materialised each frame, streaming paints are coalesced, and the live block re-renders on a short throttle — so render cost stays bounded as history grows. Thinking collapses to keep the transcript readable, and running tools and thinking blocks animate with flowing spinners.
305
+ - `lib/metrics.js` folds durable step/chunk/message events into token, TTFT, throughput, and cache-hit metrics.
190
306
  - `lib/interrupt.js` owns the clear/cancel/double-exit state machine used by stdin and `SIGINT`.
191
- - `lib/updates.js` isolates every npm/pnpm interaction for the Update settings tab: registry queries, version parsing/comparison (dependency-free semver core + pre-release), dsh install detection, and async installs — all through `child_process.spawn`, never `spawnSync`.
192
307
  - `lib/markdown.js` renders model output (headings, lists, quotes, code, inline spans) to styled lines.
193
- - Agents are created/resumed through `ctx.agents`, the transcript is rebuilt from `session.surface` on resume and fed live by `session/event` (including `assistant/chunk` streaming), model defaults come from `ctx.agentDefaultModel`, and approvals answer the `approval/request` waterfall inline.
308
+ - `lib/updates.js` isolates every npm/pnpm interaction for the Update tab registry queries, dependency-free semver comparison, dsh install detection, and installs all through `child_process.spawn`, never `spawnSync`.
309
+ - Agents are created and resumed through `ctx.agents`, the transcript is rebuilt from the session's durable log and fed live by `session/event` (including `assistant/chunk`), model defaults come from `ctx.agentDefaultModel`, and approvals answer the `approval/request` waterfall inline.
310
+ - `ask_user_question` is answered over the `user-questions/request` waterfall: a scoped Cordis waterfall where the modal either returns an answer or delegates with `next()`. An aborted request rejects so the service reports its own `ASK_ABORTED`; requests addressed to another agent are delegated untouched.
194
311
 
195
312
  ## Development
196
313
 
197
314
  ```sh
198
- node tests/smoke.test.mjs # standalone pure-module tests (no dsh needed)
199
- node --check lib/*.js # syntax
200
- node --check bin/*.js # launcher syntax
315
+ npm run check # node --check over lib/, bin/
316
+ npm test # standalone smoke tests (no dsh needed)
201
317
  ```
202
318
 
203
- The full end-to-end path (profile bootsessionlive LLM streaming commands clean exit) was verified through a pseudo-terminal (node-pty + ConPTY) on Windows.
319
+ **The install is a build, so edit buildinstall.** The profile contains a *tarball* copy of the plugin, and the profile's HMR root is the profile directory, not the plugin directory — editing this checkout changes nothing until you repack and reinstall:
320
+
321
+ ```sh
322
+ npm pack # -> dsh-oc-tui-<version>.tgz
323
+ dsh plugin --profile tui remove -w dsh-oc-tui # detach the old copy FIRST
324
+ Remove-Item .\*.tgz # then drop the stale tarball
325
+ npm pack
326
+ dsh plugin --profile tui add -w .\dsh-oc-tui-<version>.tgz
327
+ ```
204
328
 
205
- For a zero-install development bootstrap that avoids pnpm, create the profile once and point its `cordis.patch.yml` at this checkout with absolute module paths:
329
+ Detach before deleting: pnpm resolves the profile's existing `file:` dependency while adding, so a dependency pointing at a deleted tarball aborts the whole install with `ENOENT`.
330
+
331
+ Verify the swap actually landed — the version string proves nothing:
332
+
333
+ ```powershell
334
+ foreach ($rel in @('lib\index.js','lib\ui.js','lib\util.js','lib\term.js','lib\metrics.js',
335
+ 'lib\interrupt.js','lib\web-settings.js','lib\updates.js','lib\markdown.js',
336
+ 'lib\startup.js','bin\dsh-oc-tui.js','cordis.patch.yml')) {
337
+ $a = (Get-FileHash ".\$rel").Hash
338
+ $b = (Get-FileHash "$env:USERPROFILE\.dsh\profiles\tui\node_modules\dsh-oc-tui\$rel").Hash
339
+ if ($a -ne $b) { "DIFFERS: $rel" }
340
+ }
341
+ ```
342
+
343
+ Then boot it for real. Reaching the title screen is not enough — the session-open path is where host API breaks surface, so send a message. Test `--resume` separately, because it is an apply-time path that can lose a startup race the post-boot paths win.
344
+
345
+ For a zero-install bootstrap that skips packaging entirely, create the profile once and point its patch at this checkout:
206
346
 
207
347
  ```sh
208
348
  dsh --profile tui --dump-config # initializes the base profile once
209
349
  ```
210
350
 
211
- Then add to `$DSH_HOME/profiles/tui/cordis.patch.yml`:
212
-
213
351
  ```yaml
352
+ # $DSH_HOME/profiles/tui/cordis.patch.yml
214
353
  - insert:
215
354
  - id: tui-startup
216
355
  name: 'file:///D:/Projects/DeepSeekHarnessPlugins/dsh-oc-tui/lib/startup.js'
@@ -221,34 +360,55 @@ Then add to `$DSH_HOME/profiles/tui/cordis.patch.yml`:
221
360
  showReasoning: true
222
361
  ```
223
362
 
224
- The plugin's dsh imports resolve through the shared `$DSH_HOME/profiles/node_modules` fallback that dsh maintains, so no pnpm install into the plugin directory is required.
363
+ The plugin's dsh imports resolve through the shared `$DSH_HOME/profiles/node_modules` fallback that dsh maintains, so nothing has to be installed into the plugin directory.
364
+
365
+ ## Troubleshooting
366
+
367
+ | Symptom | Cause and fix |
368
+ | --- | --- |
369
+ | `pnpm failed in profile directory` / `ERR_PNPM_ADDING_TO_ROOT` | The profile is a pnpm workspace root; add `-w` to the `add`/`remove` command. |
370
+ | `ENOENT: … dsh-oc-tui-<v>.tgz` during install | The profile still references a tarball you deleted. `dsh plugin --profile tui remove -w dsh-oc-tui`, then add again. |
371
+ | `pnpm not found on PATH` | Install pnpm (`npm install -g pnpm`) and retry. |
372
+ | `--dump-config` has no TUI layer | The install did not complete, or the package name is misspelled. Re-run the `add` and check `dsh.profile.bundles`. |
373
+ | Exits immediately / no UI | stdin and stdout must both be a TTY — do not pipe or redirect. Then verify the model route and credentials exist. |
374
+ | `--resume` or Manage sessions unavailable | Both need the shared `sessionQuery` service; keep `@deepseek-ai/dsh-base` first in `dsh.profile.bundles`. |
375
+ | `no agent factory registered` on `--resume` | A startup race with the agent-loop row; current builds retry it. On an older build, run `/resume <id>` after boot instead. |
376
+ | Loader errors after updating dsh (`State`, `./internal`) | The global dsh install is a mixed old/new tree. Close every dsh process and reinstall: `npm install -g @deepseek-ai/dsh@<version>`. |
377
+ | Source edits have no effect | The installed copy is a tarball; repack and reinstall (see [Development](#development)). |
378
+
379
+ More detail, in Chinese: [docs/用户手册.md](docs/用户手册.md).
225
380
 
226
381
  ## Known limitations
227
382
 
228
- - IME composition and bracketed-paste image attachments are not exposed by the zero-dependency terminal engine yet.
229
- - Saved sessions are managed from Settings Manage sessions; the shared `sessionQuery` service is required for the list.
230
- - `dsh tui` as a bare subcommand needs a shell alias — the stock launcher hardcodes only `web` and `plugin` subcommands.
231
- - Editing the plugin source does not hot-reload (the profile's HMR root is the profile dir, not the plugin dir); restart the profile to pick up changes.
232
- - `--resume` and Settings Manage sessions require the shared `sessionQuery` service (mounted by `dsh-base`).
233
- - The deferred dsh install on Windows waits for the TUI that scheduled it, not for every dsh process on the machine with several TUI windows (or a `dsh web`) open, close them before the exit-time install runs; if a concurrent install still corrupts the tree, the next Update page visit flags it (`Install damaged reinstall below`) instead of reporting a bogus success.
383
+ - IME composition is not exposed by the zero-dependency terminal engine yet. Pasted images are: a bracketed paste of raw image bytes, a `data:image/...;base64,...` URL, a local image path, or an image URL becomes a `[Image N]` attachment, and pasting text nothing recognizes asks the terminal for its clipboard (OSC 52).
384
+ - The plugin does not hot-reload: the profile's HMR root is the profile directory, so a running TUI keeps the copy it booted with.
385
+ - `dsh tui` as a bare subcommand needs a shell alias — the stock launcher hardcodes only `web` and `plugin`.
386
+ - Harness slash commands need a live session; on the title screen the TUI tells you to start one first.
387
+ - Deferring a question with `Esc` does not cancel the tool call — it delegates, and with no other answerer the tool call fails. Per-question skip (as the Web UI composer offers) is not implemented.
388
+ - `--resume`, Settings Manage sessions, the context meter, and the stats strip depend on services mounted by `@deepseek-ai/dsh-base` (`sessionQuery`, `sessionProjections`); a hand-built profile must provide them. The `sessionStats` projection is a web-app-layer row, so the TUI folds those figures from the session log itself when no profile mounts it.
389
+ - The deferred dsh install on Windows waits for the TUI that scheduled it, not for every dsh process on the machine — close other TUI windows (and `dsh web`) before it runs.
234
390
 
235
391
  ## Layout
236
392
 
237
393
  ```
238
- lib/index.js plugin entry: agents, events, input, commands, approvals
239
- lib/startup.js command-line provider (tuiStartup service)
240
- lib/term.js terminal engine (raw mode, screen, key decoding)
241
- lib/ui.js responsive view model + renderer
242
- lib/metrics.js durable event telemetry fold
243
- lib/interrupt.js Ctrl+C lifecycle state
244
- lib/web-settings.js shared WebUI settings projection
245
- lib/updates.js in-app update manager (npm registry + installs)
246
- lib/markdown.js markdown -> styled lines
247
- lib/util.js text/display helpers
248
- bin/dsh-oc-tui.js convenience launcher for `dsh --profile tui`
249
- install.sh one-command installer (Linux/macOS)
250
- install.ps1 one-command installer (Windows)
251
- cordis.patch.yml bundle patch layer (TUI rows)
252
- docs/用户手册.md Chinese user manual
253
- tests/smoke.test.mjs standalone smoke tests
394
+ lib/index.js plugin entry: agents, events, input, commands, approvals, user questions
395
+ lib/startup.js command-line provider (tuiStartup service)
396
+ lib/term.js terminal engine (raw mode, screen, key decoding)
397
+ lib/ui.js responsive view model + renderer (includes the question modal)
398
+ lib/metrics.js whole-session stats + token usage fold (web stats strip / tokenUsage port)
399
+ lib/interrupt.js Ctrl+C lifecycle state
400
+ lib/web-settings.js shared WebUI settings projection
401
+ lib/updates.js in-app update manager (npm registry + installs)
402
+ lib/markdown.js markdown -> styled lines
403
+ lib/util.js text/display helpers
404
+ bin/dsh-oc-tui.js convenience launcher for `dsh --profile tui`
405
+ install.sh one-command installer (Linux/macOS)
406
+ install.ps1 one-command installer (Windows)
407
+ cordis.patch.yml bundle patch layer (TUI rows, agent-presets roster, ask-user tool)
408
+ docs/用户手册.md Chinese user manual
409
+ tests/smoke.test.mjs standalone smoke tests
254
410
  ```
411
+
412
+ ## License
413
+
414
+ [LGPL-3.0-or-later](LICENSE).
package/cordis.patch.yml CHANGED
@@ -22,3 +22,11 @@
22
22
  name: '@deepseek-ai/dsh-agent-presets'
23
23
  config:
24
24
  default: standard
25
+
26
+ # The model-facing `ask_user_question` tool. dsh-base mounts the
27
+ # `user-questions` service but NOT this tool — the agent presets mount it,
28
+ # and a TUI session composes process-wide from the base rather than from a
29
+ # preset, so the row must be declared here or the tool is simply absent.
30
+ # lib/index.js answers the `user-questions/request` waterfall with a modal.
31
+ - id: tool-ask-user
32
+ name: '@deepseek-ai/dsh-tool-ask-user'