@songtonyli/dsh-cli 0.1.10 → 0.1.11

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
@@ -26,7 +26,7 @@ workspace and no build step. Native packages that ship one build per platform
26
26
  ripgrep, the PTY backend's addons, sharp, koffi, and the Landlock launcher — are
27
27
  ordinary optional dependencies, so npm resolves the right one for your machine.
28
28
 
29
- Requires Node `^22.19.0 || >=24.0.0`. Version 0.1.10; `dsh --version`
29
+ Requires Node `^22.19.0 || >=24.0.0`. Version 0.1.11; `dsh --version`
30
30
  also reports the harness build the package was assembled from.
31
31
 
32
32
  ## License
package/bin/dsh.mjs CHANGED
@@ -18,7 +18,7 @@ const argv = process.argv.slice(2)
18
18
  // version this package publishes. Reporting only one of the two would make an
19
19
  // ordinary `npm install @songtonyli/dsh-cli@x` look like it installed something else.
20
20
  if (argv.length === 1 && (argv[0] === '--version' || argv[0] === '-V')) {
21
- console.log('@songtonyli/dsh-cli 0.1.10 (deepseek-harness 0.1.6-alpha.1)')
21
+ console.log('@songtonyli/dsh-cli 0.1.11 (deepseek-harness 0.1.6-alpha.1)')
22
22
  process.exit(0)
23
23
  }
24
24
 
@@ -100,7 +100,7 @@ window.__ModuleLoader__.load({
100
100
  const SCROLLBAR_LINGER_MS = 2e3;
101
101
  /** Format complete-build metadata for the local brand badge. */
102
102
  function localBuildVersion() {
103
- return `0.1.6-alpha.1-057f07b-dirty`;
103
+ return `0.1.6-alpha.1-9ed61dc-dirty`;
104
104
  }
105
105
  /** Each panel row subscribes only to its own selection state. */
106
106
  function PanelRow({ id, label, wide, usePanelInfo, selectPanel, renderSlot }) {
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/bundle/tui-app/README.md
5
- README.md: 1176042ca87a4a439e65818c9333b9004ea3519e
6
- README.zh.md: a2d0e296e2e1e5aaf7e3568727fd57e30c2e538b
5
+ README.md: f99f5ebde7ad398611e91f9e80529503423ee7f9
6
+ README.zh.md: 37fdaf5839a39657d560ee5c56c733d13dddd762
@@ -40,7 +40,9 @@ On quit the app prints `dsh: session <id> saved; resume with: dsh --profile tui
40
40
 
41
41
  ### The screen
42
42
 
43
- The header names the session by its title once one is generated or set, with the id beside it. The transcript grows in the terminal's own scrollback: your prompts start with `›` (attachments listed under them), assistant reasoning is dim above the Markdown reply, and each tool call is a card with a status glyph, the tool name, the presenter headline, and a body folded to `toolPreviewLines` rows. Below the transcript sit a spinner while the agent works, any open prompt, the editor, and a two-line footer with the model and reasoning effort, the permission preset, cumulative token usage, the context window percentage, todo and goal and plan-mode markers from the projection seam, the workspace, the count of pending attachments, and the key hints. Compaction and model-request retries appear as notices, the same facts the browser's markers carry.
43
+ The header names the session by its title once one is generated or set, with the id beside it. The transcript grows in the terminal's own scrollback: your prompts start with `›` (attachments listed under them), assistant reasoning is dim above the Markdown reply, and each tool call is a card with a status glyph, the tool name, the presenter headline, and a body folded to `toolPreviewLines` rows. Streamed reply text fades in: the newest words arrive near the terminal's background color and brighten to the normal foreground over `streamFadeSteps` levels of `streamFadeStepMs` each, so a soft edge trails the stream head, and text that has settled is never dimmed again. Below the transcript sit a spinner while the agent works, any open prompt, the editor, the subagent panel, and the footer: a status bar of segments over a line of key hints. The editor's caret is the terminal's own blinking bar: the app asks for that shape at start, gives your default back on quit, and draws no caret at all while the status bar or the panel holds the keyboard. Each segment appears only when its fact exists — the model and reasoning effort, the permission preset, the running turn's elapsed time, cumulative token usage, the context window percentage, todo and goal and plan-mode markers from the projection seam, the workspace path (shortened with `~` and `…/` when long), and the count of pending attachments and `Shift+Up` moves focus into the bar to read a segment's details. Compaction and model-request retries appear as notices, the same facts the browser's markers carry.
44
+
45
+ The subagent panel is drawn while a subagent session under the bound one is resident, or the listing carries a candidate it could not read. Its heading counts what it lists, and each row gives the child's depth indent, its label or id, its mode (`one-shot` or `continuable`), `resident`, whether its agent is `running` or `idle`, its elapsed time — the open turn's, else the total its settled turns took — and its token usage, as far as this process's own view of that child and the composed projections carry them. Six rows are drawn at most, with `+<n> more · /subagents lists them all` under them; a candidate the listing could not interpret draws as `unreadable: <reason>` and opens nothing, and a failed listing keeps the rows the last good one produced with `listing failed: <reason>` beneath. The panel disappears with its last row.
44
46
 
45
47
  ### Keys and commands
46
48
 
@@ -49,19 +51,27 @@ The header names the session by its title once one is generated or set, with the
49
51
  | `Enter` | Send the editor text; while a turn runs it is queued for the next turn |
50
52
  | `Ctrl+S` | While a turn runs, steer the editor text into the running turn's next step |
51
53
  | `Shift+Enter` | Insert a newline |
52
- | `Shift+Tab` | Cycle the current model's reasoning effort for the next request |
54
+ | `Shift+Tab` | While the editor has focus, cycle the current model's reasoning effort for the next request |
53
55
  | `Up` / `Down` | Recall earlier prompts |
56
+ | `Shift+Up` | Move focus on through the docked regions: editor → status bar → subagent panel → editor |
57
+ | `Shift+Down` | Move focus back through that cycle; in the editor with no panel drawn it stays the editor's own key |
54
58
  | `Esc` | Stop the running turn; queued messages stay queued |
55
59
  | `Ctrl+O` | Expand or collapse every tool card |
56
60
  | `Ctrl+C` | Clear the editor; a second press within 600 ms quits |
57
61
  | `Ctrl+D` | Quit when the editor is empty |
58
62
 
63
+ While the status bar has focus, `Left` / `Right` and `Tab` / `Shift+Tab` move between segments and wrap at both ends, `Enter` opens the selected segment while the bar keeps focus, `Shift+Up` moves on to the subagent panel while it is drawn, and `Esc` or `Shift+Down` returns focus to the editor. No other key reaches the editor while the bar has focus; `Ctrl+C` and `Ctrl+D` keep their usual meaning and return focus to the editor.
64
+
65
+ While the subagent panel has focus, `Up` / `Down` move the selection and wrap at both ends, `Enter` opens that child's session details as a read-only page and comes back to the panel on the same row, `Shift+Down` moves on to the status bar, and `Esc` or `Shift+Up` returns focus to the editor. Every other key is consumed there as well, apart from `Ctrl+C` and `Ctrl+D`; the panel also hands the keyboard back to the editor when its last row leaves.
66
+
67
+ Every segment except `todo` prints its details into the transcript, stating its current facts and naming what changes them: the model segment names `/model`, the effort segment `Shift+Tab`, and the permission segment its preset, the `turn` segment — drawn as `turn <elapsed>` between the permission and usage segments, and only while a turn runs — gives the turn number, its start time, its elapsed time, and the queued-message counts, while the usage, context, goal, and plan segments print the matching sections of the `/status` report, the workspace segment the full path, and the attachments segment the pending attachments. The `todo` segment opens the agent's todo list instead, the same list `/todos` opens.
68
+
59
69
  Typing `/` at the start of the editor completes the terminal's own commands and the shared registry's; `@` anywhere completes references.
60
70
 
61
71
  | Command | Effect |
62
72
  |---|---|
63
73
  | `/help` | List commands and keys |
64
- | `/model` | Pick the model, then its reasoning effort when the model declares more than one, for the next request; `/model <provider>/<model>` selects directly and `/model save` stores the current selection as the default |
74
+ | `/model` | Pick the model (type to filter the rows), then its reasoning effort when the model declares more than one, for the next request; `/model <provider>/<model>` selects directly and `/model save` stores the current selection as the default |
65
75
  | `/sessions` | Pick another persisted session and switch to it |
66
76
  | `/new` | Start a new session |
67
77
  | `/fork [turn]` | Fork this session at its last completed turn, or after turn `turn` |
@@ -73,9 +83,10 @@ Typing `/` at the start of the editor completes the terminal's own commands and
73
83
  | `/login` | Sign in with a provider subscription (hides key-collecting logins); `/login <key>` skips the picker |
74
84
  | `/export [dir]` | Write this session's log ZIP (sub-sessions and attachments included) into `dir`, default the workspace |
75
85
  | `/status` | Context window usage and breakdown, token totals with cache hit, session stats, todos, goal, plan mode, and permission |
86
+ | `/todos` | Browse the agent's todo list; `Enter` opens one item in full with its status, position, and turns |
76
87
  | `/outline` | The turns of this session with their prompt and reply previews |
77
88
  | `/deliverables` | The files the agent presented, grouped by turn |
78
- | `/subagents` | The subagent sessions under this session, with activity and ids |
89
+ | `/subagents` | Browse the subagent sessions under this session; `Enter` opens one session's details |
79
90
  | `/settings [ns [path value]]` | List namespaces, show one, or set one field; `/settings reset <ns>` restores defaults |
80
91
  | `/plugins` | The composed plugins with enablement and lifecycle phase |
81
92
  | `/tools` | Expand or collapse every tool card, like `Ctrl+O` |
@@ -83,6 +94,10 @@ Typing `/` at the start of the editor completes the terminal's own commands and
83
94
 
84
95
  Every other `/name` line goes to the shared command registry, so `/compact`, `/permission`, `/goal`, and plugin commands work as they do in the browser.
85
96
 
97
+ Every picker the terminal opens — the model and reasoning-effort lists of `/model`, `/sessions`, the `/subagents` and `/todos` lists, and the rows `/signin` and `/login` raise — filters its rows as you type: the query matches each row's label and description together, its whitespace- and slash-separated tokens must all match, and the rows are ordered best match first, so `dsk chat` and `deepseek/chat` both find `deepseek/deepseek-chat` and `gpt5` finds `gpt-5`. `Backspace` drops the last character, `Ctrl+U` clears the query, `Esc` clears a non-empty query and cancels the picker once the query is empty, `Up` / `Down` move within the matches, and `Enter` picks the highlighted row. The dim line above the rows reads `type to filter · Enter selects · Esc cancels` while the query is empty and `filter: <query> · <kept>/<total>` afterwards; a query nothing matches draws `no row matches "<query>"` in place of the rows, and the `✓` on the row in force — the row a picker opens on — shows only while the query is empty.
98
+
99
+ `/subagents`, `/todos`, and the `todo` status-bar segment share one list-then-details interaction: the picker lists the entries, `Enter` opens the highlighted entry as a read-only page, `Up` / `Down` and `PageUp` / `PageDown` scroll that page, and `Enter`, `Esc`, or `Left` returns to the list on the entry just read, so walking several entries costs no retyped command; `Esc` at the list returns to the editor. An entry whose details cannot be read says so on its page instead of closing the list.
100
+
86
101
  ### Subscription sign-in
87
102
 
88
103
  `/login` stores a subscription credential but does not activate a dormant model route. Configure the catalog route first, then use its full credential key; for example, run `/settings llm-pi-ai providers.openai-codex {}` and then `/login llm-pi-ai/openai-codex`. A marked authorization page opens in the local default browser while its URL remains in the transcript as a fallback. SSH launches, hosts without a desktop, `--no-open`, and opener failures leave the manual URL and device-code paths available instead.
@@ -102,6 +117,10 @@ An approval request draws `Allow <tool>?` with the asker's reason, the logged ca
102
117
  | `prompt` | none | A first prompt submitted when the terminal is up |
103
118
  | `resume` | none | A persisted session id to continue instead of starting a new one |
104
119
  | `toolPreviewLines` | `8` | Collapsed tool-card body rows before `Ctrl+O` expands them |
120
+ | `liveRefreshMs` | `1000` | Period of the redraw that advances the `turn` segment and the panel's elapsed values and re-reads a stale subagent listing |
121
+ | `streamFadeSteps` | `5` | Brightness levels streamed assistant text climbs before it draws in the normal foreground |
122
+ | `streamFadeStepMs` | `40` | How long one brightness level lasts, which is also the repaint period of the fading text |
123
+ | `reducedMotion` | `false` | Draw streamed text at the normal foreground, with no fade and no repeating repaint |
105
124
  | `openBrowser` | `true` | Hand marked authorization pages to the local default browser |
106
125
 
107
126
  `prompt`, `resume`, and `openBrowser` come from the command line through the startup provider; the generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tui-app) is the exhaustive source for every accepted field.
@@ -122,7 +141,7 @@ The runner awaits the complete application (`ctx.get('loader')?.await()`) and bu
122
141
 
123
142
  ### Rendering model
124
143
 
125
- Durable facts come from the session log: `user/message` (own submissions are drawn once and their echo skipped by message id; a plugin notice is one dim row and other injected context is not drawn), `assistant/message` (which replaces the streamed block with the committed text and folds usage into the footer), `tool/call` and `tool/result` (drawn through the tool's `presentCall` and `presentResult` views when it declares them, with a raw-argument and raw-result fallback), `turn/end` notices, `session/title` (header), and `permission/preset` (footer). Live incrementality comes from `agent/assistant-stream` text and reasoning deltas. Session facts outside the log come from the same services the browser reads: `sessionTitle`, `permissionPresets`, `sessionQuery` for the picker and `/deliverables`, `sessionProjections` for the footer, `/status`, and `/outline`, `fileReferences` and `sessionReferenceResolver` for `@` completion, `attachments`, `skills`, `authorization`, `settings`, `subagents`, and the Loader's entries for `/plugins`. Modal prompts are process-local presentation and are never logged.
144
+ Durable facts come from the session log: `user/message` (own submissions are drawn once and their echo skipped by message id; a plugin notice is one dim row and other injected context is not drawn), `assistant/message` (which replaces the streamed block with the committed text and folds usage into the footer), `tool/call` and `tool/result` (drawn through the tool's `presentCall` and `presentResult` views when it declares them, with a raw-argument and raw-result fallback), `turn/end` notices, `session/title` (header), `permission/preset` (footer), and `todo/write` under the enclosing `turn/start` (the turns one todo item's page reports; the list carries no per-item identity, so a reworded item counts as a new one). Live incrementality comes from `agent/assistant-stream` text and reasoning deltas. Session facts outside the log come from the same services the browser reads: `sessionTitle`, `permissionPresets`, `sessionQuery` for the picker, `/deliverables`, and subagent details, `sessionProjections` for the footer, `/status`, `/todos`, and `/outline`, `fileReferences` and `sessionReferenceResolver` for `@` completion, `attachments`, `skills`, `authorization`, `settings`, `subagents`, and the Loader's entries for `/plugins`. Modal prompts are process-local presentation and are never logged.
126
145
 
127
146
  ### Patch surface over base
128
147
 
@@ -139,18 +158,23 @@ The patch rides over `dsh-base`: it sets the coding persona prefix and cwd suffi
139
158
  | [`src/attach.ts`](src/attach.ts) | `/attach`: local files into image or file blocks through the attachment store |
140
159
  | [`src/export.ts`](src/export.ts) | `/export`: the session-log ZIP written through the export package's archive helpers |
141
160
  | [`src/blocks.ts`](src/blocks.ts) | Transcript components: user prompt, assistant reply, tool card, notice |
142
- | [`src/prompts.ts`](src/prompts.ts) | Approval, question, and picker prompts plus the modal queue |
161
+ | [`src/fade.ts`](src/fade.ts) | The streamed-text fade: the tail tracker, the background-to-foreground ramp, and the recolor of rendered lines |
162
+ | [`src/prompts.ts`](src/prompts.ts) | Approval, question, picker, and read-only detail prompts plus the modal queue |
143
163
  | [`src/transcript.ts`](src/transcript.ts) | Pure text folding of presentation views, usage, and turn-end reasons |
144
164
  | [`src/diff.ts`](src/diff.ts) | Line diff and hunk selection for diff cards |
145
165
  | [`src/style.ts`](src/style.ts) | The palette and the derived pi-tui themes |
146
166
  | [`src/completion.ts`](src/completion.ts) | Slash-command and `@`-reference completion for the editor |
147
- | [`src/status.ts`](src/status.ts) | Footer parts and the `/status` report over the projection seam; compaction and retry notices |
167
+ | [`src/editor.ts`](src/editor.ts) | The prompt editor without pi-tui's drawn block cursor, and the DECSCUSR sequences for the terminal's own caret |
168
+ | [`src/status.ts`](src/status.ts) | Projection-seam facts and the sections the `/status` report and the segment details share; compaction and retry notices |
169
+ | [`src/footer.ts`](src/footer.ts) | The status bar: the ordered segments, each segment's detail rows, and the footer's two rendered lines |
170
+ | [`src/subagent-panel.ts`](src/subagent-panel.ts) | The live subagent panel: one descendant listing plus sampled live facts become its rows, and the rows its text |
148
171
  | [`src/catalog.ts`](src/catalog.ts) | Rows for `/settings`, `/plugins`, `/subagents`, `/deliverables`, and `/outline` |
172
+ | [`src/todos.ts`](src/todos.ts) | The todo list: the status glyphs, the picker rows, and one item's detail rows |
149
173
  | [`cordis.patch.yml`](cordis.patch.yml) | The terminal patch over `dsh-base` |
150
174
  | — | No runtime invariant companion is published; the app registers listeners on one Agent and holds no mutable relation another observer could contradict. |
151
175
  | [`tests/app.spec.ts`](tests/app.spec.ts) | Rendering, keys, commands, and both seams over a fake terminal |
152
176
  | [`tests/commands.spec.ts`](tests/commands.spec.ts) | Session, attachment, queue, skill, sign-in, `/login`, Shift+Tab effort cycling, export, reference, and effort commands over scripted services |
153
- | [`tests/panels.spec.ts`](tests/panels.spec.ts) | Status footer and report, catalog commands, command hints, and the approval detail |
177
+ | [`tests/panels.spec.ts`](tests/panels.spec.ts) | Status footer and report, the navigable subagent and todo lists, catalog commands, command hints, and the approval detail |
154
178
  | [`tests/index.spec.ts`](tests/index.spec.ts) | Creation, resume paging, fork cut, session switching, quit flow, and failure reporting |
155
179
  | [`tests/startup.spec.ts`](tests/startup.spec.ts) | Command-line parsing over a real Loader tree |
156
180
  | [`../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts`](../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts) | The shipped profile through the real launcher with a keyless mock model |
@@ -201,6 +225,11 @@ These limits describe the terminal surface as shipped; they are not a general CL
201
225
  - **Browser-only pages stay in the browser** — workspace and directory pickers, open-in-app links, the trajectory ledger, and per-message like/dislike have no terminal counterpart; `/settings`, `/plugins`, `/subagents`, `/outline`, and the shared `/feedback` cover their facts as text, and subagent transcripts are read by switching to the child session.
202
226
  - **Deliverables are named, not opened** — `/deliverables` lists the presented paths; the browser previews the files.
203
227
  - **Terminal scrollback owns history** — the transcript is not searchable or foldable beyond tool cards; the browser surface owns richer navigation.
228
+ - **The panel lists residency, not the tree** — a child joins it while its session record is resident in this process, so a subagent run by an out-of-process provider, which owns no session here, never appears; `/subagents` remains the way to every durable descendant.
229
+ - **Residency is not work** — the listing's `activity: 'running'` says the child's record is resident, which is what the row's `resident` reports; whether the child is working is the separate `running` / `idle` word beside it, read from that child's Agent in this process.
230
+ - **Rows behind the overflow row are not selectable** — the panel draws at most six rows and `Up` / `Down` wrap inside them; the children folded into `+<n> more` are reached through `/subagents`, which walks the complete descendant tree.
231
+ - **The fade needs an answer from the terminal** — its ramp is built from the background color the terminal reports to the query sent at startup, so a terminal that stays silent, or that encodes neither truecolor nor 256 colors, gets the two-level faint mode instead; `NO_COLOR`, a disabled palette, `TERM=dumb`, and `reducedMotion` turn the effect off entirely.
232
+ - **The terminal's own caret can flicker** — the editor draws no caret of its own and the app turns the terminal cursor on, which pi-tui then moves across the lines it repaints; a terminal that does not honor the synchronized-output sequences pi-tui wraps a frame in can show that movement.
204
233
  - **Runs through the `dsh` launcher** — starting the profile another way fails at startup, because only the launcher can request the process exit.
205
234
 
206
235
  <a id="dev-note"></a>
@@ -40,7 +40,9 @@ dsh tui --no-open # print sign-in URLs without opening a
40
40
 
41
41
  ### 屏幕布局
42
42
 
43
- 页眉在标题生成或设置后以标题命名会话,并在旁边显示 id。对话记录在终端自身的回滚区中增长:你的提示以 `›` 开头(附件列在其下),assistant 的推理以暗色显示在 Markdown 回复上方,每次工具调用是一张卡片,含状态符号、工具名、呈现器标题,以及折叠到 `toolPreviewLines` 行的正文。对话记录下方依次是 agent 工作时的旋转指示、任何打开的提示、编辑器,以及两行页脚:模型与推理强度、权限预设、累计 token 用量、上下文窗口百分比、来自投影接缝的 todo、目标与计划模式标记、workspace、待发送附件数量与按键提示。压缩与模型请求重试以通知形式出现,与浏览器标记承载的事实相同。
43
+ 页眉在标题生成或设置后以标题命名会话,并在旁边显示 id。对话记录在终端自身的回滚区中增长:你的提示以 `›` 开头(附件列在其下),assistant 的推理以暗色显示在 Markdown 回复上方,每次工具调用是一张卡片,含状态符号、工具名、呈现器标题,以及折叠到 `toolPreviewLines` 行的正文。流式回复的文字会淡入:最新的词以接近终端背景色的亮度出现,并经 `streamFadeSteps` 级亮度、每级 `streamFadeStepMs` 提亮到正常前景色,因此流头之后拖着一条柔和的边缘,而已经稳定下来的文字不会再被调暗。对话记录下方依次是 agent 工作时的旋转指示、任何打开的提示、编辑器、子 agent 面板,以及页脚:一行分段状态栏,其下是一行按键提示。编辑器的光标是终端自身的闪烁竖条:应用在启动时请求这一形状,退出时把你的默认形状还回去,而状态栏或面板持有键盘期间完全不绘制光标。每个分段只在其事实存在时出现——模型与推理强度、权限预设、正在进行轮次的已用时间、累计 token 用量、上下文窗口百分比、来自投影接缝的 todo、目标与计划模式标记、workspace 路径(过长时以 `~` 与 `…/` 缩短)、待发送附件数量——`Shift+Up` 把焦点移入状态栏以查看某个分段的详情。压缩与模型请求重试以通知形式出现,与浏览器标记承载的事实相同。
44
+
45
+ 子 agent 面板在绑定会话之下有常驻的子 agent 会话、或列表中带有无法读取的候选者时绘制。其标题统计所列出的条目,每一行给出该子会话的层级缩进、其标签或 id、其模式(`one-shot` 或 `continuable`)、`resident`、其 agent 处于 `running` 还是 `idle`、其已用时间——进行中轮次的用时,否则是已结束轮次的合计——以及其 token 用量,以本进程对该子会话的可见程度与已组合的投影所能提供的为限。最多绘制六行,其下是 `+<n> more · /subagents lists them all`;列表无法解读的候选者绘制为 `unreadable: <reason>` 且打不开任何页面,读取失败的列表则保留上一次成功读取产生的各行,并在其下写出 `listing failed: <reason>`。面板随其最后一行一同消失。
44
46
 
45
47
  ### 按键与命令
46
48
 
@@ -49,19 +51,27 @@ dsh tui --no-open # print sign-in URLs without opening a
49
51
  | `Enter` | 发送编辑器文本;轮次进行中时它排队到下一轮次 |
50
52
  | `Ctrl+S` | 轮次进行中时,把编辑器文本引导(steer)进当前轮次的下一步 |
51
53
  | `Shift+Enter` | 插入换行 |
52
- | `Shift+Tab` | 循环切换当前模型的推理强度,从下一次请求生效 |
54
+ | `Shift+Tab` | 编辑器持有焦点时,循环切换当前模型的推理强度,从下一次请求生效 |
53
55
  | `Up` / `Down` | 调出先前的提示 |
56
+ | `Shift+Up` | 在停靠区域之间向前移动焦点:编辑器 → 状态栏 → 子 agent 面板 → 编辑器 |
57
+ | `Shift+Down` | 沿该循环反向移动焦点;在编辑器中且未绘制面板时,它仍是编辑器自己的按键 |
54
58
  | `Esc` | 停止正在进行的轮次;已排队的消息保持排队 |
55
59
  | `Ctrl+O` | 展开或折叠所有工具卡片 |
56
60
  | `Ctrl+C` | 清空编辑器;600 ms 内再按一次则退出 |
57
61
  | `Ctrl+D` | 编辑器为空时退出 |
58
62
 
63
+ 状态栏持有焦点时,`Left` / `Right` 与 `Tab` / `Shift+Tab` 在分段之间移动并在两端环绕,`Enter` 打开所选分段且状态栏保持焦点,`Shift+Up` 在面板已绘制时继续移动到子 agent 面板,`Esc` 或 `Shift+Down` 把焦点交还编辑器。状态栏持有焦点期间其他按键不会到达编辑器;`Ctrl+C` 与 `Ctrl+D` 保持其一贯含义,并把焦点交还编辑器。
64
+
65
+ 子 agent 面板持有焦点时,`Up` / `Down` 移动选择并在两端环绕,`Enter` 把该子会话的详情作为只读页面打开、离开后回到面板的同一行,`Shift+Down` 继续移动到状态栏,`Esc` 或 `Shift+Up` 把焦点交还编辑器。除 `Ctrl+C` 与 `Ctrl+D` 外,其他按键同样在此被消费;面板的最后一行离开时,它也会把键盘交还编辑器。
66
+
67
+ 除 `todo` 之外的每个分段都把详情打印到对话记录,陈述其当前事实并指出改变它们的方式:模型分段指出 `/model`,推理强度分段指出 `Shift+Tab`,权限分段指出其预设;`turn` 分段——绘制在权限与用量分段之间、形如 `turn <elapsed>`,且只在轮次进行时出现——给出轮次编号、其开始时间、已用时间与排队消息数量;用量、上下文、目标与计划分段打印 `/status` 报告中对应的小节,workspace 分段显示完整路径,附件分段列出待发送的附件。`todo` 分段则改为打开 agent 的 todo 列表,与 `/todos` 打开的是同一个列表。
68
+
59
69
  在编辑器开头输入 `/` 会补全终端自身的命令与共享注册表的命令;任意位置的 `@` 补全引用。
60
70
 
61
71
  | 命令 | 效果 |
62
72
  |---|---|
63
73
  | `/help` | 列出命令与按键 |
64
- | `/model` | 为下一次请求选择模型,若模型声明多于一种推理强度则接着选择强度;`/model <provider>/<model>` 直接选择,`/model save` 把当前选择存为默认 |
74
+ | `/model` | 为下一次请求选择模型(输入即可过滤行),若模型声明多于一种推理强度则接着选择强度;`/model <provider>/<model>` 直接选择,`/model save` 把当前选择存为默认 |
65
75
  | `/sessions` | 选择另一个持久化会话并切换过去 |
66
76
  | `/new` | 开始新会话 |
67
77
  | `/fork [turn]` | 在本会话最后一个完成轮次处 fork,或在第 `turn` 轮之后 fork |
@@ -73,9 +83,10 @@ dsh tui --no-open # print sign-in URLs without opening a
73
83
  | `/login` | 用提供方订阅登录(隐藏仅收集密钥的登录);`/login <key>` 跳过选择器 |
74
84
  | `/export [dir]` | 把本会话的日志 ZIP(含子会话与附件)写入 `dir`,默认 workspace |
75
85
  | `/status` | 上下文窗口用量与构成、含缓存命中的 token 总计、会话统计、todo、目标、计划模式与权限 |
86
+ | `/todos` | 浏览 agent 的 todo 列表;`Enter` 完整打开其中一条,含其状态、位置与轮次 |
76
87
  | `/outline` | 本会话各轮次及其提示与回复预览 |
77
88
  | `/deliverables` | agent 交付的文件,按轮次分组 |
78
- | `/subagents` | 本会话之下的子 agent 会话,含活动状态与 id |
89
+ | `/subagents` | 浏览本会话之下的子 agent 会话;`Enter` 打开某个会话的详情 |
79
90
  | `/settings [ns [path value]]` | 列出命名空间、显示某一个或设置某个字段;`/settings reset <ns>` 恢复默认 |
80
91
  | `/plugins` | 已组合的插件及其启用状态与生命周期阶段 |
81
92
  | `/tools` | 像 `Ctrl+O` 一样展开或折叠所有工具卡片 |
@@ -83,6 +94,10 @@ dsh tui --no-open # print sign-in URLs without opening a
83
94
 
84
95
  其他每条 `/name` 行都交给共享命令注册表,因此 `/compact`、`/permission`、`/goal` 与插件命令的行为和浏览器中一致。
85
96
 
97
+ 终端打开的每个选择器——`/model` 的模型列表与推理强度列表、`/sessions`、`/subagents` 与 `/todos` 的列表,以及 `/signin` 与 `/login` 引出的各行——都随输入过滤其行:查询同时匹配每行的标签与描述,其以空白与斜杠分隔的各段必须全部匹配,各行按最佳匹配在前排序,因此 `dsk chat` 与 `deepseek/chat` 都能找到 `deepseek/deepseek-chat`,`gpt5` 能找到 `gpt-5`。`Backspace` 删除最后一个字符,`Ctrl+U` 清空查询,`Esc` 在查询非空时清空查询、在查询为空时取消选择器,`Up` / `Down` 在匹配行之间移动,`Enter` 选中高亮行。行上方的暗色行在查询为空时显示 `type to filter · Enter selects · Esc cancels`,此后显示 `filter: <query> · <kept>/<total>`;无任何行匹配的查询会以 `no row matches "<query>"` 取代这些行,而生效行上的 `✓`——也就是选择器打开时定位的那一行——只在查询为空时显示。
98
+
99
+ `/subagents`、`/todos` 与状态栏的 `todo` 分段共用同一套先列表、后详情的交互:选择器列出各条目,`Enter` 把高亮条目作为只读页面打开,`Up` / `Down` 与 `PageUp` / `PageDown` 滚动该页面,`Enter`、`Esc` 或 `Left` 返回列表并停在刚读过的条目上,因此连续查看多个条目无需重新输入命令;在列表上按 `Esc` 返回编辑器。详情无法读取的条目会在其页面上说明原因,而不会关闭列表。
100
+
86
101
  ### 订阅登录
87
102
 
88
103
  `/login` 只存储订阅凭据,不会激活休眠的模型路由。先配置 catalog 路由,再使用完整的凭据键;例如先运行 `/settings llm-pi-ai providers.openai-codex {}`,再运行 `/login llm-pi-ai/openai-codex`。被标记的授权页面会在本地默认浏览器中打开,其 URL 同时保留在对话记录中作为后备。SSH 启动、无桌面的宿主、`--no-open` 以及打开器失败时,手动 URL 与设备码路径仍然可用。
@@ -102,6 +117,10 @@ dsh tui --no-open # print sign-in URLs without opening a
102
117
  | `prompt` | 无 | 终端就绪后提交的首个提示 |
103
118
  | `resume` | 无 | 要继续的持久化会话 id,而不是新建会话 |
104
119
  | `toolPreviewLines` | `8` | `Ctrl+O` 展开前折叠的工具卡片正文行数 |
120
+ | `liveRefreshMs` | `1000` | 重绘周期:推进 `turn` 分段与面板中的已用时间,并重新读取已过期的子 agent 列表 |
121
+ | `streamFadeSteps` | `5` | 流式 assistant 文本在以正常前景色绘制之前经过的亮度级数 |
122
+ | `streamFadeStepMs` | `40` | 每级亮度持续多久,这也是淡入文字的重绘周期 |
123
+ | `reducedMotion` | `false` | 以正常前景色绘制流式文本,不做淡入,也不重复重绘 |
105
124
  | `openBrowser` | `true` | 把被标记的授权页面交给本地默认浏览器 |
106
125
 
107
126
  `prompt`、`resume` 与 `openBrowser` 经启动提供方来自命令行;生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tui-app)是所有可接受字段的完整来源。
@@ -122,7 +141,7 @@ runner 等待完整应用就绪(`ctx.get('loader')?.await()`),并在核心
122
141
 
123
142
  ### 渲染模型
124
143
 
125
- 持久事实来自会话日志:`user/message`(自己提交的消息只绘制一次,其回显按消息 id 跳过;插件通知是一行暗色文字,其他注入的上下文不绘制)、`assistant/message`(用已提交文本替换流式块,并把用量折入页脚)、`tool/call` 与 `tool/result`(工具声明 `presentCall` 与 `presentResult` 视图时据此绘制,否则回退到原始参数与原始结果)、`turn/end` 通知、`session/title`(页眉)与 `permission/preset`(页脚)。实时增量来自 `agent/assistant-stream` 的文本与推理增量。日志之外的会话事实来自浏览器读取的同一批服务:`sessionTitle`、`permissionPresets`、供选择器与 `/deliverables` 使用的 `sessionQuery`、供页脚、`/status` 与 `/outline` 使用的 `sessionProjections`、供 `@` 补全使用的 `fileReferences` 与 `sessionReferenceResolver`、`attachments`、`skills`、`authorization`、`settings`、`subagents`,以及供 `/plugins` 使用的 Loader 条目。模态提示是进程本地的呈现,从不写入日志。
144
+ 持久事实来自会话日志:`user/message`(自己提交的消息只绘制一次,其回显按消息 id 跳过;插件通知是一行暗色文字,其他注入的上下文不绘制)、`assistant/message`(用已提交文本替换流式块,并把用量折入页脚)、`tool/call` 与 `tool/result`(工具声明 `presentCall` 与 `presentResult` 视图时据此绘制,否则回退到原始参数与原始结果)、`turn/end` 通知、`session/title`(页眉)、`permission/preset`(页脚),以及所属 `turn/start` 之下的 `todo/write`(某条 todo 的详情页所报告的轮次;该列表不带逐条标识,因此改写措辞的条目算作新条目)。实时增量来自 `agent/assistant-stream` 的文本与推理增量。日志之外的会话事实来自浏览器读取的同一批服务:`sessionTitle`、`permissionPresets`、供选择器、`/deliverables` 与子 agent 详情使用的 `sessionQuery`、供页脚、`/status`、`/todos` 与 `/outline` 使用的 `sessionProjections`、供 `@` 补全使用的 `fileReferences` 与 `sessionReferenceResolver`、`attachments`、`skills`、`authorization`、`settings`、`subagents`,以及供 `/plugins` 使用的 Loader 条目。模态提示是进程本地的呈现,从不写入日志。
126
145
 
127
146
  ### 基于 base 的 patch 面
128
147
 
@@ -139,18 +158,23 @@ runner 等待完整应用就绪(`ctx.get('loader')?.await()`),并在核心
139
158
  | [`src/attach.ts`](src/attach.ts) | `/attach`:本地文件经附件存储成为图片或文件块 |
140
159
  | [`src/export.ts`](src/export.ts) | `/export`:通过导出包的归档辅助函数写出会话日志 ZIP |
141
160
  | [`src/blocks.ts`](src/blocks.ts) | 对话记录组件:用户提示、assistant 回复、工具卡片、通知 |
142
- | [`src/prompts.ts`](src/prompts.ts) | 审批、提问与选择器提示以及模态队列 |
161
+ | [`src/fade.ts`](src/fade.ts) | 流式文本淡入:尾部追踪器、从背景色到前景色的亮度级别,以及对已渲染行的重新着色 |
162
+ | [`src/prompts.ts`](src/prompts.ts) | 审批、提问、选择器与只读详情提示以及模态队列 |
143
163
  | [`src/transcript.ts`](src/transcript.ts) | 呈现视图、用量与轮次结束原因的纯文本折叠 |
144
164
  | [`src/diff.ts`](src/diff.ts) | diff 卡片的行 diff 与 hunk 选择 |
145
165
  | [`src/style.ts`](src/style.ts) | 调色板与派生的 pi-tui 主题 |
146
166
  | [`src/completion.ts`](src/completion.ts) | 编辑器的斜杠命令与 `@` 引用补全 |
147
- | [`src/status.ts`](src/status.ts) | 基于投影接缝的页脚部件与 `/status` 报告;压缩与重试通知 |
167
+ | [`src/editor.ts`](src/editor.ts) | 去掉 pi-tui 自绘块状光标的提示编辑器,以及终端自身光标所用的 DECSCUSR 序列 |
168
+ | [`src/status.ts`](src/status.ts) | 投影接缝的事实,以及 `/status` 报告与分段详情共享的小节;压缩与重试通知 |
169
+ | [`src/footer.ts`](src/footer.ts) | 状态栏:有序的各分段、每个分段的详情行,以及页脚渲染出的两行 |
170
+ | [`src/subagent-panel.ts`](src/subagent-panel.ts) | 实时子 agent 面板:一次后代列表加上采样到的实时事实构成其各行,各行再构成其文本 |
148
171
  | [`src/catalog.ts`](src/catalog.ts) | `/settings`、`/plugins`、`/subagents`、`/deliverables` 与 `/outline` 的行 |
172
+ | [`src/todos.ts`](src/todos.ts) | todo 列表:状态符号、选择器行与单个条目的详情行 |
149
173
  | [`cordis.patch.yml`](cordis.patch.yml) | 基于 `dsh-base` 的终端 patch |
150
174
  | — | 不发布运行时不变量伴随模块;应用只在一个 Agent 上注册监听器,不持有其他观察者可能与之矛盾的可变关系。 |
151
175
  | [`tests/app.spec.ts`](tests/app.spec.ts) | 基于伪终端的渲染、按键、命令与两个接缝 |
152
176
  | [`tests/commands.spec.ts`](tests/commands.spec.ts) | 基于脚本化服务的会话、附件、队列、技能、登录、`/login`、Shift+Tab 推理强度循环、导出、引用与推理强度命令 |
153
- | [`tests/panels.spec.ts`](tests/panels.spec.ts) | 状态页脚与报告、目录命令、命令提示与审批详情 |
177
+ | [`tests/panels.spec.ts`](tests/panels.spec.ts) | 状态页脚与报告、可导航的子 agent 与 todo 列表、目录命令、命令提示与审批详情 |
154
178
  | [`tests/index.spec.ts`](tests/index.spec.ts) | 创建、恢复分页、fork 切割、会话切换、退出流程与失败报告 |
155
179
  | [`tests/startup.spec.ts`](tests/startup.spec.ts) | 基于真实 Loader 配置树的命令行解析 |
156
180
  | [`../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts`](../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts) | 通过真实启动器与免密钥模拟模型运行随附 profile |
@@ -201,6 +225,11 @@ runner 不向请求前缀添加任何内容;`/model` 切换像在浏览器中
201
225
  - **仅浏览器的页面留在浏览器**——workspace 与目录选择器、在应用中打开的链接、轨迹账本与逐条消息的点赞/点踩没有终端对应物;`/settings`、`/plugins`、`/subagents`、`/outline` 与共享的 `/feedback` 以文本覆盖其事实,子 agent 的对话记录通过切换到子会话来阅读。
202
226
  - **交付物只列名、不打开**——`/deliverables` 列出交付路径;浏览器会预览这些文件。
203
227
  - **历史由终端回滚区持有**——除工具卡片外,对话记录不可搜索或折叠;更丰富的导航由浏览器表层持有。
228
+ - **面板列出的是常驻,而不是整棵树**——子会话在其会话记录常驻于本进程期间加入面板,因此由进程外提供方运行、在此没有自己会话的子 agent 永远不会出现;`/subagents` 仍是到达每一个持久后代的途径。
229
+ - **常驻不等于正在工作**——列表的 `activity: 'running'` 表示该子会话的记录常驻,这正是行内 `resident` 所报告的内容;子会话是否在工作则是它旁边单独的 `running` / `idle`,读自本进程中该子会话的 Agent。
230
+ - **溢出行之后的各行不可选中**——面板最多绘制六行,`Up` / `Down` 只在这些行内环绕;被折叠进 `+<n> more` 的子会话通过 `/subagents` 到达,后者遍历完整的后代树。
231
+ - **淡入需要终端的应答**——其亮度级别由终端对启动时发出的查询所报告的背景色构建,因此保持沉默、或既不编码真彩色也不编码 256 色的终端只会得到两级的暗淡模式;`NO_COLOR`、被禁用的调色板、`TERM=dumb` 与 `reducedMotion` 则完全关闭该效果。
232
+ - **终端自身的光标可能闪烁**——编辑器不绘制自己的光标,应用打开终端光标,而 pi-tui 会在其重绘的各行之间移动它;不支持 pi-tui 为一帧包裹的同步输出序列的终端可能显示出这种移动。
204
233
  - **通过 `dsh` 启动器运行**——以其他方式启动该 profile 会在启动时失败,因为只有启动器能请求进程退出。
205
234
 
206
235
  <a id="dev-note"></a>