@sagmans/dsh-tui 0.2.0 → 0.4.0

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.
Files changed (174) hide show
  1. package/README.md +282 -25
  2. package/cordis.patch.yml +14 -0
  3. package/lib/agent/model.d.ts +26 -0
  4. package/lib/agent/model.d.ts.map +1 -1
  5. package/lib/agent/model.js +41 -0
  6. package/lib/agent/model.js.map +1 -1
  7. package/lib/agent/projections.d.ts +22 -6
  8. package/lib/agent/projections.d.ts.map +1 -1
  9. package/lib/agent/projections.js +21 -10
  10. package/lib/agent/projections.js.map +1 -1
  11. package/lib/agent/prompt-history.d.ts +89 -0
  12. package/lib/agent/prompt-history.d.ts.map +1 -0
  13. package/lib/agent/prompt-history.js +380 -0
  14. package/lib/agent/prompt-history.js.map +1 -0
  15. package/lib/agent/status.d.ts +16 -0
  16. package/lib/agent/status.d.ts.map +1 -1
  17. package/lib/agent/status.js +3 -0
  18. package/lib/agent/status.js.map +1 -1
  19. package/lib/cards.d.ts +1 -1
  20. package/lib/cards.d.ts.map +1 -1
  21. package/lib/cards.js +4 -3
  22. package/lib/cards.js.map +1 -1
  23. package/lib/gates.d.ts +145 -2
  24. package/lib/gates.d.ts.map +1 -1
  25. package/lib/gates.js +335 -40
  26. package/lib/gates.js.map +1 -1
  27. package/lib/herdr/client.d.ts +57 -0
  28. package/lib/herdr/client.d.ts.map +1 -0
  29. package/lib/herdr/client.js +218 -0
  30. package/lib/herdr/client.js.map +1 -0
  31. package/lib/herdr/constants.d.ts +93 -0
  32. package/lib/herdr/constants.d.ts.map +1 -0
  33. package/lib/herdr/constants.js +79 -0
  34. package/lib/herdr/constants.js.map +1 -0
  35. package/lib/herdr/reporter.d.ts +60 -0
  36. package/lib/herdr/reporter.d.ts.map +1 -0
  37. package/lib/herdr/reporter.js +217 -0
  38. package/lib/herdr/reporter.js.map +1 -0
  39. package/lib/herdr/state.d.ts +57 -0
  40. package/lib/herdr/state.d.ts.map +1 -0
  41. package/lib/herdr/state.js +62 -0
  42. package/lib/herdr/state.js.map +1 -0
  43. package/lib/index.d.ts.map +1 -1
  44. package/lib/index.js +631 -118
  45. package/lib/index.js.map +1 -1
  46. package/lib/input/actions.d.ts +136 -0
  47. package/lib/input/actions.d.ts.map +1 -0
  48. package/lib/input/actions.js +743 -0
  49. package/lib/input/actions.js.map +1 -0
  50. package/lib/input/ghost.d.ts +46 -0
  51. package/lib/input/ghost.d.ts.map +1 -0
  52. package/lib/input/ghost.js +58 -0
  53. package/lib/input/ghost.js.map +1 -0
  54. package/lib/input/keymap-settings.d.ts +21 -0
  55. package/lib/input/keymap-settings.d.ts.map +1 -0
  56. package/lib/input/keymap-settings.js +28 -0
  57. package/lib/input/keymap-settings.js.map +1 -0
  58. package/lib/input/keymap.d.ts +91 -0
  59. package/lib/input/keymap.d.ts.map +1 -0
  60. package/lib/input/keymap.js +172 -0
  61. package/lib/input/keymap.js.map +1 -0
  62. package/lib/input/match.d.ts +10 -0
  63. package/lib/input/match.d.ts.map +1 -0
  64. package/lib/input/match.js +83 -0
  65. package/lib/input/match.js.map +1 -0
  66. package/lib/input/submission.d.ts +29 -1
  67. package/lib/input/submission.d.ts.map +1 -1
  68. package/lib/input/submission.js +33 -2
  69. package/lib/input/submission.js.map +1 -1
  70. package/lib/input.d.ts +17 -0
  71. package/lib/input.d.ts.map +1 -0
  72. package/lib/input.js +27 -0
  73. package/lib/input.js.map +1 -0
  74. package/lib/keys-command.d.ts +16 -0
  75. package/lib/keys-command.d.ts.map +1 -0
  76. package/lib/keys-command.js +60 -0
  77. package/lib/keys-command.js.map +1 -0
  78. package/lib/queue.d.ts +6 -0
  79. package/lib/queue.d.ts.map +1 -0
  80. package/lib/queue.js +68 -0
  81. package/lib/queue.js.map +1 -0
  82. package/lib/settings-notice.d.ts +19 -0
  83. package/lib/settings-notice.d.ts.map +1 -0
  84. package/lib/settings-notice.js +30 -0
  85. package/lib/settings-notice.js.map +1 -0
  86. package/lib/stash/lock.d.ts +72 -0
  87. package/lib/stash/lock.d.ts.map +1 -0
  88. package/lib/stash/lock.js +374 -0
  89. package/lib/stash/lock.js.map +1 -0
  90. package/lib/stash/paths.d.ts +22 -0
  91. package/lib/stash/paths.d.ts.map +1 -0
  92. package/lib/stash/paths.js +83 -0
  93. package/lib/stash/paths.js.map +1 -0
  94. package/lib/stash/private-fs.d.ts +61 -0
  95. package/lib/stash/private-fs.d.ts.map +1 -0
  96. package/lib/stash/private-fs.js +379 -0
  97. package/lib/stash/private-fs.js.map +1 -0
  98. package/lib/stash/schema.d.ts +57 -0
  99. package/lib/stash/schema.d.ts.map +1 -0
  100. package/lib/stash/schema.js +127 -0
  101. package/lib/stash/schema.js.map +1 -0
  102. package/lib/stash/store.d.ts +92 -0
  103. package/lib/stash/store.d.ts.map +1 -0
  104. package/lib/stash/store.js +262 -0
  105. package/lib/stash/store.js.map +1 -0
  106. package/lib/stash.d.ts +91 -0
  107. package/lib/stash.d.ts.map +1 -0
  108. package/lib/stash.js +296 -0
  109. package/lib/stash.js.map +1 -0
  110. package/lib/theme-settings.d.ts +72 -8
  111. package/lib/theme-settings.d.ts.map +1 -1
  112. package/lib/theme-settings.js +125 -5
  113. package/lib/theme-settings.js.map +1 -1
  114. package/lib/theme-tokens.d.ts +4 -4
  115. package/lib/theme-tokens.d.ts.map +1 -1
  116. package/lib/theme-tokens.js +39 -5
  117. package/lib/theme-tokens.js.map +1 -1
  118. package/lib/todo-guard.d.ts +116 -0
  119. package/lib/todo-guard.d.ts.map +1 -0
  120. package/lib/todo-guard.js +259 -0
  121. package/lib/todo-guard.js.map +1 -0
  122. package/lib/ui/editor.d.ts +58 -1
  123. package/lib/ui/editor.d.ts.map +1 -1
  124. package/lib/ui/editor.js +174 -19
  125. package/lib/ui/editor.js.map +1 -1
  126. package/lib/ui/frame.d.ts +54 -0
  127. package/lib/ui/frame.d.ts.map +1 -0
  128. package/lib/ui/frame.js +79 -0
  129. package/lib/ui/frame.js.map +1 -0
  130. package/lib/ui/gate-input.d.ts +20 -0
  131. package/lib/ui/gate-input.d.ts.map +1 -0
  132. package/lib/ui/gate-input.js +96 -0
  133. package/lib/ui/gate-input.js.map +1 -0
  134. package/lib/ui/history-picker.d.ts +15 -0
  135. package/lib/ui/history-picker.d.ts.map +1 -0
  136. package/lib/ui/history-picker.js +26 -0
  137. package/lib/ui/history-picker.js.map +1 -0
  138. package/lib/ui/markdown.d.ts +11 -2
  139. package/lib/ui/markdown.d.ts.map +1 -1
  140. package/lib/ui/markdown.js +25 -6
  141. package/lib/ui/markdown.js.map +1 -1
  142. package/lib/ui/mermaid.d.ts +44 -0
  143. package/lib/ui/mermaid.d.ts.map +1 -0
  144. package/lib/ui/mermaid.js +178 -0
  145. package/lib/ui/mermaid.js.map +1 -0
  146. package/lib/ui/picker.d.ts +40 -8
  147. package/lib/ui/picker.d.ts.map +1 -1
  148. package/lib/ui/picker.js +79 -24
  149. package/lib/ui/picker.js.map +1 -1
  150. package/lib/ui/prompt.d.ts +32 -0
  151. package/lib/ui/prompt.d.ts.map +1 -0
  152. package/lib/ui/prompt.js +49 -0
  153. package/lib/ui/prompt.js.map +1 -0
  154. package/lib/ui/queue.d.ts +24 -0
  155. package/lib/ui/queue.d.ts.map +1 -0
  156. package/lib/ui/queue.js +57 -0
  157. package/lib/ui/queue.js.map +1 -0
  158. package/lib/ui/stash-picker.d.ts +43 -0
  159. package/lib/ui/stash-picker.d.ts.map +1 -0
  160. package/lib/ui/stash-picker.js +78 -0
  161. package/lib/ui/stash-picker.js.map +1 -0
  162. package/lib/ui/status.d.ts +6 -0
  163. package/lib/ui/status.d.ts.map +1 -1
  164. package/lib/ui/status.js +15 -0
  165. package/lib/ui/status.js.map +1 -1
  166. package/lib/ui/view.d.ts +22 -2
  167. package/lib/ui/view.d.ts.map +1 -1
  168. package/lib/ui/view.js +84 -24
  169. package/lib/ui/view.js.map +1 -1
  170. package/lib/work.d.ts +51 -1
  171. package/lib/work.d.ts.map +1 -1
  172. package/lib/work.js +53 -1
  173. package/lib/work.js.map +1 -1
  174. package/package.json +7 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Interactive terminal (TUI) surface for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): use `dsh` in a terminal instead of a browser.
4
4
 
5
- Status: **v1 feature-complete; published on npm as `@sagmans/dsh-tui`.** The surface owns the alternate screen, streams assistant text as markdown, renders every tool's own card, answers approvals and questions, restores and names stored conversations, switches model mid-session, runs any of the four shipped agent modes and switches between them before a session's first turn, reads a child agent's conversation in place, keeps the goal, plan mode, todo list, delegations, and background jobs above the editor with a status line below it, and hands the terminal back on every graceful exit. Publication is tag-driven with GitHub OIDC provenance and no stored npm token; see [RELEASE.md](RELEASE.md).
5
+ Status: **v1 feature-complete; published on npm as `@sagmans/dsh-tui`.** The surface owns the alternate screen, streams assistant text as markdown, renders every tool's own card, answers approvals and questions, restores and names stored conversations, switches model mid-session, runs any of the four shipped agent modes and switches between them before a session's first turn, reads a child agent's conversation in place, keeps the goal, plan mode, todo list, delegations, and background jobs above the editor with a status line below it, nudges an agent whose plan has aged without an update, parks and restores prompt drafts per working directory, and hands the terminal back on every graceful exit. Publication is tag-driven with GitHub OIDC provenance and no stored npm token; see [RELEASE.md](RELEASE.md).
6
6
 
7
7
  ## Install
8
8
 
@@ -117,26 +117,37 @@ dsh --profile tui --no-color
117
117
  dsh --profile tui --no-bell # do not ring when a long turn finishes
118
118
  ```
119
119
 
120
+ Every key below is a shipped default. `/keys` lists every action the surface
121
+ and its library can perform with the keys in force, and the `keys:` section
122
+ moves any of them — see [Keys](#keys).
123
+
120
124
  | Key | Action |
121
125
  |---|---|
122
- | Enter | submit the prompt |
126
+ | Enter / Shift+Enter | break the line: a prompt is written before it is sent |
127
+ | Ctrl+Enter / Alt+Enter / Ctrl+S | submit the prompt |
123
128
  | Ctrl+C | interrupt the running turn, or leave when idle |
124
129
  | Ctrl+O | open every tool card: its header plus every retained row. Folded, a card is one line, and a shell card keeps its command plus the last 20 rows of output with a hint naming what it dropped |
125
130
  | Ctrl+Y | show or hide the calls a PTC program dispatched: one two-space-indented entry per call under its `run_code` card, named and argued from the tool's own header and wrapped at the screen edge; shown by default |
126
131
  | Shift+Tab | expand or fold the reasoning behind an answer: folded, the row names itself, its token count, and the key; opened, it adds the thought |
127
132
  | Ctrl+T | pick the reasoning effort for the next step |
133
+ | Ctrl+R | reverse-search recorded prompts: the list opens filtered by whatever is in the bar, `enter` puts one back, `esc` keeps the draft |
134
+ | Ctrl+X then S | stash the current draft |
135
+ | Ctrl+X then L | open this directory's stashed drafts |
136
+ | Ctrl+X then M | open the model picker |
137
+ | Ctrl+X then Y | copy the last answer to the clipboard |
128
138
  | `y` / `n` / Esc | allow once, reject, or cancel a pending approval |
129
- | digits / space / ↑↓ / Enter / Esc | answer a question: pick or toggle, confirm, or skip one |
139
+ | digits / space / ↑↓ / Enter / Esc | answer a question: pick or toggle, confirm, or skip one; `0` answers with your own text in the input bar |
140
+ | typing in any picker or question | narrow the rows by fragment (`glm53` finds `GLM-5.3`); backspace widens, `esc` or Ctrl+C leaves |
130
141
  | `/` then Tab | complete commands, including every command this session registered |
131
142
  | `@` or a path then Tab | complete workspace file references |
132
143
  | `ctrl+shift+f` | search the transcript (`enter` next, `shift+enter` previous, `esc` close) |
133
144
  | `home` / `end` | jump to the start or the end of the transcript |
134
145
  | `ctrl+down` | jump to the next prompt |
135
- | `ctrl+b` | leave a child's conversation and return to this session |
146
+ | `ctrl+b` | leave a child's conversation and return to this session (the status line names the key you have now) |
136
147
  | mouse wheel, drag | scroll, and copy a selection through OSC 52 |
137
148
  | `/help` | list registered and local commands |
138
149
  | `/status` | show the session id, model, permissions, context, and directory |
139
- | `/model` | show the route the next step will use, and the providers available |
150
+ | `/model` | open the picker for the configured providers and their models; it heads itself with the route the next step will use, and typing filters it by fragment (`glm53` finds `GLM-5.3`) |
140
151
  | `/model <provider>` | list that provider's advertised models |
141
152
  | `/model <provider>/<model>` | use that route from the next step on (session only, nothing is written to settings) |
142
153
  | `/model <provider>/<model>/<effort>` | use that route and reasoning effort (the effort must be one the route advertises) |
@@ -152,21 +163,138 @@ dsh --profile tui --no-bell # do not ring when a long turn finishes
152
163
  | `/export [path]` | write the visible transcript as markdown (default `dsh-session-<id>.md`) |
153
164
  | `/resume` | open another stored session without leaving the terminal |
154
165
  | `/clear` | clear the visible transcript |
166
+ | `/history` | show how many prompts are recorded and where the file is |
167
+ | `/history clear` | forget every recorded prompt, reporting how many went |
155
168
  | `/theme` | list every styled element and the value in force |
169
+ | `/keys` | list every action and the keys in force; `/keys <layer>` narrows it (see [Keys](#keys)) |
170
+ | `/stash <draft>` | park the text given after the command (`ctrl+x` then `s` parks the editor) |
171
+ | `/stash-pop [index\|id]` | put a stashed draft into the editor and remove it (newest by default) |
172
+ | `/stash-apply [index\|id]` | put a stashed draft into the editor and keep it |
173
+ | `/stash-list` | pick from this directory's stashed drafts; `enter` pops the marked one |
174
+ | `/stash-drop [index\|id]` | delete a stashed draft without using it |
175
+ | `/stash-clear` | delete every stashed draft for this directory, after a confirmation |
156
176
  | `/quit` | leave and print the resume command |
157
177
 
158
- Any other `/command` goes to the command registry, so `/plan`, `/compact`, `/goal`, and `/feedback` behave as they do on the other surfaces.
178
+ `Ctrl+X` starts a chord. For the next two seconds the footer leads with the
179
+ prefix alone — enough to say that a key is waiting, without reciting the map —
180
+ and a key that finishes nothing is typed as usual rather than swallowed, so a
181
+ prefix pressed by accident costs nothing; `/help` lists the chords, `m` for the
182
+ model picker, `p` for plan mode, `y` for the last answer, `s` to stash the
183
+ draft, and `l` for the stashes.
184
+ `keys.chord.prefix: alt+x` starts the chord with another key — or with a list of
185
+ them, as so many ways in — and `prefixWindow: 0` waits for the next key instead
186
+ of lapsing; every second key is a row of its own (`chord.model`, `chord.plan`,
187
+ `chord.copy`, `chord.stash`, `chord.stashes`), so a chord can be respelled whole. A prefix that is not a modifier chord, that
188
+ the surface or the prompt bar already answers (`ctrl+c`, `ctrl+s`), or that the
189
+ terminal keeps (`ctrl+q`) is refused with the reason, and the shipped keymap
190
+ stays in force. The chords themselves are the commands they stand for: `m`, `p`,
191
+ `y`, `s`, and `l` ask the same dispatcher `/model`, `/plan`, `/copy`, `/stash`,
192
+ and `/stash-list` do. Plan mode is
193
+ the one pair that cannot share a name: `/plan` only enters, so the chord names
194
+ `/plan off` instead when the agent is in plan mode — or is waiting for the turn
195
+ boundary to become so — and reads that state from the plan package rather than
196
+ from the dock.
197
+
198
+ An approval or a question draws inline above the editor and takes the keyboard. A question that lists options always adds row `0. other — type your own answer`: type or paste an answer the model did not offer, and the seam receives it as that question's free text — replacing a single-select choice, or supplementing a multi-select one. `0`, or `↓` past the last option, reaches the row; `↑` walks back to the list with the text kept, and `esc` does the same from that row, because a question skipped by accident is a question answered twice — an escape from the list skips it. Free text is written in the prompt bar's own editor, drawn under that row: movement, word and line deletion, undo, completion, and multi-line paste are all the editor the reader already uses, and the prompt bar steps aside while a question is open, so a prompt written but not sent comes back untouched once the question is answered. No question hides its answer — the reader is the one who has to check what they are about to send. Every gate row wraps at the screen edge under its own label, so a long option or question is readable rather than cut.
199
+
200
+ While a turn runs, a prompt submitted into the editor waits in the agent's own inbox instead of disappearing: it is drawn above the editor in the input bar's own frame, faint and italic, and moves into the transcript when the agent takes it — where it keeps that frame in the prompt's own mint shade, so what the reader typed is never mistaken for what the agent said. `editor.queued` and `editor.queued.more` restyle or hide the waiting rows; `transcript.user` restyles the submitted prompt.
201
+
202
+ Every submitted line is also kept in a global prompt history at
203
+ `$DSH_HOME/prompt-history.json`. Typing the start of a prompt that was sent
204
+ before draws the rest of the newest match after the cursor in a faint shade.
205
+ `Ctrl+E` takes the whole suggestion and the word-movement key takes the next
206
+ word, and both keys fall back to their old meaning the moment nothing is
207
+ offered. The history is deliberately global — the same prompt is useful in
208
+ every checkout — so nothing records a directory. An exact repeat moves to the
209
+ front instead of being stored twice. `history.ghost: false` keeps reverse
210
+ search but stops drawing the suggestion, `history.enabled: false` stops
211
+ recording and offering, and `history.maxEntries` bounds the file. A file this
212
+ build cannot parse is left untouched and writes are refused, so a newer format
213
+ is never overwritten; `/history` names it and the count, and `/history clear`
214
+ forgets everything.
159
215
 
160
- ## Theme
216
+ Any other `/command` goes to the command registry, so `/plan`, `/compact`, `/goal`, and `/feedback` behave as they do on the other surfaces.
161
217
 
162
- Every styled element is a named token with a shipped default, so the surface can
163
- be restyled without touching code. Overrides live in the same user-settings
164
- document as every other preference (`$DSH_HOME/settings.yaml`), under a
165
- `dsh-tui:` section:
218
+ ## Prompt stash
219
+
220
+ `ctrl+x` then `s` parks the draft the editor is holding and clears it;
221
+ `/stash <draft>` parks a draft typed on the command line. A bare `/stash` only
222
+ says so, because submitting a command consumes the line it was typed on and there
223
+ is nothing left of the draft to park. `/stash-pop` puts a parked draft back and
224
+ removes it, so a prompt written for the wrong moment survives a restart instead
225
+ of being retyped or sent; `l` opens the list of them. A stash belongs to the exact
226
+ working directory, so the drafts parked in one checkout never appear in another;
227
+ the footer shows `stash N` while any are waiting, ranked above the context and
228
+ cache numbers it shares a row with.
229
+
230
+ A selector is the number the list shows in brackets — `0` is the newest — or the
231
+ entry's own id; leaving it out takes the newest. `apply` and `pop` refuse to
232
+ overwrite a draft already in the editor, because losing an unsent prompt to a
233
+ restore is the one outcome the feature exists to prevent. They refuse while a
234
+ question is borrowing the bar for the same reason: a draft written into an answer
235
+ would be sent as one. `pop` writes the editor first and removes the entry second,
236
+ so a crash between the two leaves the draft in the bank rather than only in a
237
+ terminal that is gone.
238
+
239
+ Nothing is cleared until the write has landed. A refusal — no room left, a bank
240
+ past its cap, another writer holding the lock — leaves the draft in the bar,
241
+ including a draft typed after `/stash`, which is written back into the bar before
242
+ the write is attempted. The bar is only cleared while it still holds that same
243
+ draft and no question has borrowed it, so an answer typed during the write is
244
+ never wiped by a stash finishing.
245
+
246
+ The bank is one JSON file per directory under `$DSH_HOME/tui-stash`, written with
247
+ owner-only permissions (`0700` directory, `0600` file) through a no-follow open,
248
+ and every directory the path passes through must be owned by the reader (or by
249
+ root) and not writable by anyone else — the sticky bit is the only exception,
250
+ since it keeps renaming to an entry's owner. Links are walked one hop at a time,
251
+ with `..` left for the filesystem to resolve against what the link points at, and
252
+ a link this user does not own ends the walk: a chain that jumps through a shared
253
+ directory is refused at the directory it jumped through. A directory
254
+ that another user or a group member could redirect the storage through is refused
255
+ rather than trusted, which is also why a group-writable home directory fails the
256
+ stash with the offending path named. Every update is a locked read-modify-write
257
+ and an atomic temp-and-rename, so two surfaces in the same directory cannot lose
258
+ each other's entries; reclaiming a lock whose owner is gone is serialized on a
259
+ per-bank claim file, and the removal only applies to the lock it judged, so a
260
+ holder that released in between cannot have its successor's live lock deleted. A
261
+ contender never deletes a lock it did not publish, so losing the name to a
262
+ successor costs a retry rather than the successor's turn.
263
+
264
+ A bank whose working directory is not this one, or whose bytes do not parse, is
265
+ moved aside as `<name>.corrupt-<time>` and reported with its path — including when
266
+ the directory holding the copy could not be synced. A bank written by a newer
267
+ format, or one past the size cap, is refused in place rather than moved, because
268
+ neither is corruption. A storage directory a save had to create is flushed
269
+ through the directory that names it before the save reports anything. If that
270
+ flush fails, the empty directories are taken back so the retry starts clean; a
271
+ directory another surface has already saved into is left exactly as it is, because
272
+ an entry left unflushed costs durability while a removed bank costs the draft. Drafts are never written to a session log, and control and
273
+ Unicode bidi controls are stripped when a draft is stored and again when it is
274
+ read, so a hand-edited bank cannot park a terminal escape or a reordering trick in
275
+ the bar.
276
+
277
+ ## Settings
278
+
279
+ Every styled element is a named token with a shipped default, and every key is
280
+ an action with one, so the surface can be restyled and rebound without touching
281
+ code. Preferences live in the same user-settings document as every other
282
+ (`$DSH_HOME/settings.yaml`), under a `dsh-tui:` section:
166
283
 
167
284
  ```yaml
168
285
  dsh-tui:
169
286
  subcalls: collapsed # fold the calls a PTC program dispatched (default inline)
287
+ mermaid: streaming # draw a reply's mermaid fences: off, final, or streaming (default streaming)
288
+ prefixWindow: 2 # seconds a chord waits for its second key; 0 waits for the next key instead
289
+ keys:
290
+ chord.prefix: ctrl+x # the key that starts a chord; "prefix:" is the older spelling of this row
291
+ prompt.submit: [ctrl+enter, alt+enter, ctrl+s]
292
+ surface.effort: ctrl+t # one key, or a list of them
293
+ tui.editor.yank: ctrl+y # any action the library draws, by the id /keys prints
294
+ history:
295
+ enabled: true # record prompts and offer them back (default true)
296
+ ghost: true # draw the dimmed completion; reverse search stays either way (default true)
297
+ maxEntries: 2000 # prompts kept, newest first (1-20000, default 2000)
170
298
  palette:
171
299
  muted: '#5c5c5c' # one shade quiets every receding element
172
300
  tokens:
@@ -181,9 +309,10 @@ dsh-tui:
181
309
  ```
182
310
 
183
311
  Every field is optional, so a section that changes one shade is enough. The
184
- document is hot-reloaded: an edit restyles a running session, and `/theme` shows
185
- each element's effective value and whether it came from an override, the
186
- palette, or the default.
312
+ document is hot-reloaded: an edit restyles a running session and re-arms the
313
+ keymap on the next press, and `/theme` shows each element's effective value and
314
+ whether it came from an override, the palette, or the default.
315
+
187
316
 
188
317
  The section is not only shades. By default every session draws the calls a PTC
189
318
  program dispatched under their card, and `subcalls: collapsed` starts with the
@@ -191,6 +320,26 @@ card alone instead. `Ctrl+Y` toggles the same choice for the current session,
191
320
  and an edit to the document re-seeds it. An unknown key or value is
192
321
  refused with a notice naming it, so a typo cannot quietly do nothing.
193
322
 
323
+ The `history` block tunes the prompt history. `enabled: false` stops recording
324
+ and offering it; `ghost: false` keeps reverse search but stops the dimmed
325
+ completion; `maxEntries` bounds the file, and an exact repeat moves to the
326
+ front rather than being stored twice. `editor.ghost` styles the suggestion, and
327
+ `NO_COLOR` or `--no-color` suppresses it entirely, because a suggestion the
328
+ reader cannot see but could still accept is worse than none.
329
+
330
+ A reply whose fenced block names `mermaid` is drawn as terminal box art instead
331
+ of source, laid out at the width the transcript has. `mermaid: streaming` (the
332
+ default) draws a diagram while the reply is still arriving, `final` waits for
333
+ the turn to end, and `off` leaves every fence exactly as written. A diagram
334
+ wider than the terminal, one the renderer cannot draw at all, or one whose source
335
+ is larger than a frame can lay out stays as the source fence rather than being
336
+ truncated; a settled diagram whose source was only partly readable keeps the
337
+ fence and names what was dropped. The drawing is
338
+ restyleable like anything else through `markdown.diagram.border`,
339
+ `.text`, `.edge`, `.edgeLabel`, `.title`, and `.warning`, so `/theme`
340
+ lists it with the rest. Nothing is lost by drawing: `/export` and the session
341
+ file keep the reply exactly as the model wrote it.
342
+
194
343
  `fg` and `bg` accept `#rrggbb`, a palette name (`default`, `muted`, `accent`,
195
344
  `arg`, `warn`, `added`, `removed`, `user`, `assistant`), or an index. A colour is
196
345
  emitted as 24-bit when the terminal advertises it (`COLORTERM`) and degraded to
@@ -200,7 +349,7 @@ name the palette rather than a terminal slot, so on anything but a 16-colour
200
349
  terminal their contrast does not depend on what the reader's colour scheme maps
201
350
  slot 8 to. `arg` is the pale blue a card gives the argument it was called with,
202
351
  so `tool.args` is restyled on its own and stays distinct from the tool's own
203
- label and from its output. `user` is the rose a submitted prompt takes, so a
352
+ label and from its output. `user` is the mint a submitted prompt takes, so a
204
353
  reader's own turns stand apart from the reply without reading either.
205
354
 
206
355
  `NO_COLOR` and `--no-color` disable styling entirely, attributes included, and
@@ -208,6 +357,49 @@ outrank everything in this section. A token or palette name the surface does not
208
357
  have is refused with the offending name, and the surface prints the refusal as a
209
358
  notice when the document loads, so a typo cannot quietly paint nothing.
210
359
 
360
+ ### Keys
361
+
362
+ Every press the surface answers is an action with an id and a shipped key.
363
+ `/keys` prints all of them with the keys in force, marks the rows you wrote,
364
+ names the keys your map took from the library, and ends with a sample `keys:`
365
+ section rather than a whole one; the table above it is the complete account.
366
+ `/keys prompt`, `surface`, `chord`, `gate`, `question`,
367
+ `picker`, and `library` narrow that list to one part of the surface.
368
+
369
+ An entry is one key or a list of them. A key is a modifier chord
370
+ (`ctrl`/`alt`/`shift` joined by `+`, written in that order), a named key
371
+ (`enter`, `escape`, `tab`, `space`, `backspace`, `delete`, `home`, `end`,
372
+ `pageUp`, `pageDown`, the arrows, `f1`–`f12`), or a bare character where the
373
+ layer reads one: `y` and `n` for an approval, or a chord's second key. Ids
374
+ beginning `tui.` are pi-tui's own actions, so the editor, the search, and the
375
+ transcript move where you tell them to.
376
+
377
+ Refused, with the reason in a notice and the shipped map left in force: an
378
+ action the surface does not have, a key no terminal reports, `ctrl+q` (the
379
+ terminal keeps it), a bare character outside the chord and gate layers, two
380
+ actions of one layer on one press, a key the library already answers on a row you
381
+ never wrote, a key the viewport reads before the surface sees it, whichever of the
382
+ two the map moved onto it (`pageUp`, or `tui.altScreen.search` moved onto a
383
+ surface key), a `chord.prefix` that is not a modifier chord or that takes a key
384
+ the surface or the prompt bar answers, and `prefix:` beside
385
+ `keys.chord.prefix:`, which are the same row under two names.
386
+
387
+ Two rows count as one press when some sequence reaches both, not merely when they
388
+ are spelled alike, because one press can arrive as several bytes and one byte can
389
+ spell several keys. A bare terminal reports Return for `enter` and `ctrl+m`, a
390
+ line feed for `ctrl+j` and, without the keyboard protocol, Return as well, one
391
+ control byte carries both `ctrl+-` and `ctrl+_`, and an escape with a letter
392
+ reaches `alt+up` as readily as `alt+p`.
393
+
394
+ A key the surface or a chord answers is a key the library never sees: that is
395
+ how `ctrl+y` shows nested calls instead of yanking a line in the editor.
396
+ `/keys` names every shadow your map introduces, and moving the surface key
397
+ hands the library its own key back.
398
+
399
+ Left alone, because they are typing rather than commands: the keys a question's
400
+ filter narrows with and the ones that leave its free-text row, the digits and
401
+ row `0` that name an option, and the mouse.
402
+
211
403
  One residual escapes that promise, and it is not the surface's to close. After a
212
404
  component returns its rows, the framework appends a reset to each row and closes
213
405
  the hyperlink it wraps them in, so a session can still receive a bare `ESC[0m`
@@ -244,17 +436,63 @@ The package is a Cordis plugin bundle that stacks over `@deepseek-ai/dsh-base`:
244
436
  - `@deepseek-ai/dsh-agent-presets` is the roster of modes, holding the id a session starts in when nobody names one.
245
437
  - `@deepseek-ai/dsh-code-runtime-worker-thread` and `@deepseek-ai/dsh-cordis-host-runner` are the host machinery PTC mode and creator mode need; only the Web bundle shipped them, so a terminal profile has to mount them to offer those modes at all.
246
438
  - `@sagmans/dsh-tui` owns the terminal: it creates or resumes one agent through `ctx.agents`, folds `session/event` into transcript rows and work state, renders them with `@earendil-works/pi-tui`, and releases the terminal on exit, on a boot failure, and on a signal.
439
+ - `@sagmans/dsh-tui/todo-guard` is the one advisory row this bundle adds to the agent plane: it watches the harness's own `todos` and `plan` projections and rides the next tool result with a reminder when a plan ages. See [Todo discipline](#todo-discipline).
440
+
441
+ A question whose id ends in `:secret` declares its typed answer a credential: the bar hides everything but its first and last four characters, and the free-text row a question with options offers is labelled `API KEY`. Wording is not a declaration, because hiding every question that mentions a key would hide answers their authors meant to be read.
247
442
 
248
443
  The fold is durable-only: the live stream decorates the row that is still being written, and everything else — cards, reasoning, work state, compaction markers — comes from the log, so a resumed session renders what the live one did. Subagent start and finish are the exception: they arrive as service events, and the transcript shows them as decoration because the durable record of a delegation is the tool call that asked for it.
249
444
 
250
445
  Tool cards are folded by default: a card draws its header and nothing else, so a long read, diff, or search cannot bury the conversation. A shell card is the exception, because its output is the answer the reader asked for: it names the tool in its header, always shows the command that ran, keeps the last 20 output rows, and adds a hint naming the rows it dropped. `Ctrl+O` opens every card to its header plus every retained row.
251
446
 
252
- A PTC card is the one card with children: every call the `run_code` program dispatched hangs off the card that made it, and each draws under the header as the tool's own name and argument, wrapping rather than being cut. `Ctrl+Y` folds them away again, and `subcalls: collapsed` starts every session folded; see [Theme](#theme).
447
+ A PTC card is the one card with children: every call the `run_code` program dispatched hangs off the card that made it, and each draws under the header as the tool's own name and argument, wrapping rather than being cut. `Ctrl+Y` folds them away again, and `subcalls: collapsed` starts every session folded; see [Settings](#settings).
253
448
 
254
449
  A card's header names the tool, then the argument the call was made with — a path or a command — in the `tool.args` colour, then the facts the result measured: a read reports its line range, line count, and token size; a file change that carried no prior content to compare against reports its lines and tokens; one that did reports added, changed, and removed lines as `+n ~n -n` in green, yellow, and red. Each stat is its own token, so any of them can be recoloured or hidden independently.
255
450
 
256
451
  The bundle also takes the base's global agent rows out of the composition, twenty-three of them. Every one is a row the shipped modes supply per session instead, so leaving it mounted registers the same tool names in two layers and doubles each prompt section it owns. What stays mounted is the host: sessions, storage, models, permissions, jobs, and the command registry.
257
452
 
453
+ ### Todo discipline
454
+
455
+ The todo tool and its list belong to the agent; this bundle owns the surface and one advisory guard. `@sagmans/dsh-tui/todo-guard` mounts host-plane, reads the harness's own `todos` and `plan` projections, and — when a non-empty list has gone a threshold of model steps without a `todo_write`, or a long turn has produced no list at all — rides the next tool result with a model-visible reminder. It never vetoes a call, never steers a stopped turn, and never adds a prompt section, so its request prefix stays stable across deployments. A reminder costs the loop one extra model step to consume; the per-turn cap bounds that. It stays silent in plan mode, in a mode whose catalog has no `todo_write`, and when the projections are absent.
456
+
457
+ | Option | Default | Effect |
458
+ |---|---|---|
459
+ | `staleSteps` | `6` | model steps a non-empty open list may age before the guard speaks |
460
+ | `missingListSteps` | `12` | steps in a turn before the guard suggests a first list |
461
+ | `maxRemindersPerTurn` | `3` | hard cap on reminders, and on the extra steps they cost, per turn |
462
+ | `previewItems` | `5` | open items quoted in a reminder; the rest become a count |
463
+
464
+ Override them from the home-level patch, which outranks the profile's own layers. An id-targeted patch replaces the whole config, so restate every field you keep:
465
+
466
+ ```yaml
467
+ # $DSH_HOME/cordis.patch.yml
468
+ - id: tui-todo-guard
469
+ config:
470
+ staleSteps: 4
471
+ missingListSteps: 12
472
+ maxRemindersPerTurn: 3
473
+ previewItems: 5
474
+ ```
475
+
476
+ The list the dock and `/todo` draw follows the same lifetime every other surface shows: it is cleared when the next turn opens, because a fresh task must not inherit the previous turn's checklist.
477
+
478
+ ## Herdr
479
+
480
+ [Herdr](https://github.com/herdrdev/herdr) is a terminal multiplexer for coding agents. When it starts this surface in one of its panes it exports `HERDR_ENV=1`, `HERDR_PANE_ID`, and `HERDR_SOCKET_PATH`, and the pane reports what it is doing over that socket — as the agent `dsh`, under the source `custom:dsh-tui`. Away from Herdr (an ordinary terminal, SSH, tmux) the reporter is inert: no socket is opened, and nothing reaches the screen the reader owns.
481
+
482
+ | This surface | Herdr |
483
+ |---|---|
484
+ | the screen is taken, before any session opens | `idle`, claiming the pane's agent row |
485
+ | `turn/start` | `working` |
486
+ | an approval, a question, or a picker takes the keyboard | `blocked`, with that card's title sent along |
487
+ | the decision settles | `working` if a turn is open, otherwise `idle` |
488
+ | `turn/end` | `idle` |
489
+ | a session opens, resumes, forks, or is switched to | its id and reason, plus the `dsh_session` / `dsh_cwd` pane tokens |
490
+ | exit, signal, or boot failure | `herdr pane release-agent`, so no row is left waiting on a process that is gone |
491
+
492
+ A wait outranks a running turn: a turn waiting on a human is not making progress, and the wait is the only thing worth acting on from a wall of panes. Reports are sequenced per source, so a delivery that arrives late cannot undo the state the surface already moved past, and a state Herdr is already showing is not sent again. A report is only counted as made when Herdr acknowledges it: one that failed is tried again — soon after, then with a growing wait — and a session identity Herdr never confirmed travels with the next report of any kind. The retry cannot wait for another state change, because the pane may have nothing left to say: a turn that ended while the socket was down produces no further event. States still waiting to be sent are collapsed into the newest one, so a socket that was down for a minute is told where the pane is rather than where it has been. The release carries the next number in that same sequence for the same reason: Herdr reads one that cannot beat the pane's last report as stale, and a stale release leaves the row waiting on a process that is gone. It also stops reporting first — a claim landing after the release would take the row back for a process that is leaving — and the report already on the wire is waited for before the row goes back, because Herdr ignores the release of a pane nothing has claimed yet and that late report would then claim it. What is still waiting behind it is dropped rather than sent.
493
+
494
+ Herdr persists a session reference only for its own built-in integrations, so this pane's session identity travels as metadata tokens instead: a script or a companion plugin reads them back with `herdr pane get <id>` and resumes that exact conversation with `dsh --profile tui --resume=<id>`. Herdr holds a token value up to 80 characters and shortens anything longer, so a session id or directory that cannot be sent whole has its token cleared instead: a shortened path would read as a different directory, and a pane must not claim one. What Herdr cannot do is identify the process itself — its detection table and its screen rules both name built-in agents only — so a pane that has not reported yet reads as an ordinary pane, and is not yet a target: `herdr agent wait` on it fails with `agent_not_found` until the first report lands. Herdr 0.9.1 also keeps the title that accompanies a `blocked` state without showing it anywhere, so a reader sees the state and reads the card on screen.
495
+
258
496
  ## Development
259
497
 
260
498
  ```sh
@@ -300,40 +538,59 @@ The automated checks drive a real PTY, but they run on this machine's terminal.
300
538
  | that turn in a terminal narrower than a call's own argument | the argument wraps onto continuation rows indented to the same two spaces, with no ellipsis |
301
539
  | that same turn, then `ctrl+y` | the lines fold away; `ctrl+y` again draws them back |
302
540
  | `dsh-tui: { subcalls: collapsed }` in `$DSH_HOME/settings.yaml`, then a PTC turn | the card arrives alone, and editing the document to `inline` unfolds a running session |
541
+ | a reply carrying a mermaid fence | it draws as box art at the transcript width, with the prose around it untouched |
542
+ | that reply in a terminal narrower than the drawing | the fence stays source, and widening the window draws it without a new turn |
543
+ | `dsh-tui: { mermaid: off }` in `$DSH_HOME/settings.yaml`, then a mermaid reply | the fence stays source; editing the value to `streaming` draws a settled reply without a restart |
544
+ | type the start of a prompt already recorded | the rest of the newest match follows the cursor in a faint shade; `ctrl+e` takes it whole, the word-right key takes one word, and both keys do their old job when nothing is offered |
545
+ | `ctrl+r`, then a fragment | reverse search opens seeded with the bar's draft; `enter` puts a prompt back, `esc` keeps the draft |
546
+ | `dsh-tui: { history: { ghost: false } }`, then type a known prefix | no suggestion is drawn, and `ctrl+r` still searches |
547
+ | `/history clear`, then `/history` | the notice reports the count forgotten, and the second reports `1 prompt recorded` — the check line is itself recorded |
548
+ | `/model` on a configured profile | the picker lists only the configured providers' advertised models, heads itself with the route in force, and typing filters it while later rows stream in; `esc` or Ctrl+C leaves without changing the route, and `enter` chains into the route's reasoning efforts |
303
549
  | `/preset` on a fresh session | the picker lists four modes, marks the current one, and the switch survives a resume |
304
550
  | `/preset minimal` after a turn | refused, naming the reason; the session keeps the mode it composed with |
305
551
  | `--resume --preset <mode>` and then picking a session that runs another mode | the list stays open and says why that row cannot be taken; `esc` leaves the picker |
306
552
  | `dsh --profile tui --preset nope` | exits non-zero naming the modes that do exist, before the alternate screen appears |
307
553
  | arrow keys in a picker, or on a question's options, in a terminal that reports key events (Kitty, WezTerm, Ghostty, iTerm2) | one press moves one row, and holding a key still repeats; a terminal that sends only the legacy sequence behaves the same |
308
554
  | resize the window mid-turn | the transcript rewraps; the dock, editor, and status row stay put |
309
- | a 40-column terminal | rows end in `…` instead of wrapping into the next line |
555
+ | a 40-column terminal | transcript and card rows end in `…` instead of wrapping into the next line |
556
+ | a question with a long option at 40 columns | the option wraps onto rows indented under its label, and `0. other — type your own answer` sits under the list |
557
+ | press `0` on a question, type an answer, press Enter | the editor under row `0` shows the text as it is edited, and the model receives it as that question's answer |
558
+ | type a prompt without sending it, then answer a question | the prompt bar steps aside while the question is open and holds the same prompt again afterwards |
310
559
  | `echo hi \| dsh --profile tui` | refuses with a non-zero exit and a message naming the TTY requirement |
560
+ | `/stash`, `/stash-pop` in one terminal | the footer shows `stash 1` after the stash and the draft returns to the editor after the pop |
561
+ | a second `dsh --profile tui` in the same directory | `/stash-list` shows the draft the first terminal parked |
562
+ | `dsh --profile tui` in another directory | `/stash-list` says `no stashed drafts`, even though the first directory still has one |
563
+ | hand-edit `$DSH_HOME/tui-stash/<key>.json` into invalid JSON, then `/stash-list` | the surface reports the quarantine path, starts empty, and leaves the moved file readable |
311
564
  | `/quit`, Ctrl+C while idle, `kill -TERM <pid>` | the shell returns with cursor, echo, mouse, and title restored |
312
565
 
313
566
  ## Releasing
314
567
 
315
568
  Published artefacts carry a provenance attestation, which only a CI provider can issue, so releases ship from the tag workflow rather than a laptop.
316
569
 
317
- 1. Bump `version` in `package.json`, land it on `main` through a reviewed PR, and wait for CI to pass on the merged SHA.
570
+ 1. Bump `version` in `package.json` and move `CHANGELOG.md`'s `Unreleased` section to that version, land both on `main` through a reviewed PR, and wait for CI to pass on the merged SHA.
318
571
  2. Tag that SHA with a signed tag and push it. The tag ruleset admits repository admins only.
319
572
  3. `.github/workflows/release.yml` re-runs typecheck, tests, and the package smoke; the publish job then waits for a maintainer's approval on the `npm-release` environment before it publishes with OIDC trusted publishing and automatic provenance.
320
573
 
321
- The workflow stores no npm token: the registry trusts `release.yml` on the `npm-release` environment, and [`scripts/npm/release.py`](scripts/npm/release.py) creates both the environment and that trust. The full runbook is [RELEASE.md](RELEASE.md).
574
+ The workflow stores no npm token: the registry trusts `release.yml` on the `npm-release` environment, and [`scripts/npm/release.py`](scripts/npm/release.py) creates both the environment and that trust. The full runbook is [RELEASE.md](RELEASE.md); the user-visible history is [CHANGELOG.md](CHANGELOG.md).
322
575
 
323
576
  ## Limitations
324
577
 
325
578
  - Two different things are called a preset. The agent mode (`--preset`, `/preset`) is fixed once a session has produced a turn; the permission preset (`/permission <preset>`, named in the status line) can change at any time.
326
- - `/model` changes the route and reasoning effort for the running session only. Catalog membership is advisory — an adapter may accept an id it does not advertise, while an explicit effort is checked against the route's own levels before it is applied.
579
+ - `/model` changes the route and reasoning effort for the running session only. Catalog membership is advisory — an adapter may accept an id it does not advertise, while an explicit effort is checked against the route's own levels before it is applied. The picker offers the routes this deployment configured, not the ones it can prove credentialed: a provider whose key or sign-in is still missing appears like any other, and its first request names the missing credential.
327
580
  - Scrolling is the mouse wheel, or the terminal's own scrollback keys where it offers them.
328
581
  - A turn that ran longer than ten seconds rings the terminal bell when it ends, because the reader may have walked away; `--no-bell` turns that off.
329
- - The dock shows the goal, plan mode, the todo items still to do, and any background job or delegation still running; a settled item leaves rather than turns into a completed row. The transcript marks where older history was compacted away. `/plan` toggles plan mode; `/plan <message>` also steers that message, which is the base command's own behaviour.
582
+ - The dock shows the goal, plan mode, the todo items still to do, and any background job or delegation still running; a settled item leaves rather than turns into a completed row, and the list clears when the next turn opens so a fresh task never inherits the previous one's checklist. The transcript marks where older history was compacted away. `/plan` toggles plan mode; `/plan <message>` also steers that message, which is the base command's own behaviour.
330
583
  - Background jobs and subagent runs are live process state, not durable events: they disappear when the run ends, and a resumed session starts with an empty board and roster.
331
584
  - A card reads its tool's own render intent through the agent whose session is on screen, so a stored session with no live agent — one this process is not running, or a child that has already finished — folds to the generic card instead of the tool's own.
332
- - Reading a child's conversation does not move the terminal: commands, approvals, and the status line stay with the session you launched, and the transcript is the only thing that switches.
585
+ - Reading a child's conversation does not move the terminal: commands, approvals, and the status line stay with the session you launched, and the transcript is the only thing that switches. The status line carries the way back, read from the map in force, so a remap shows up without reopening the view.
333
586
  - Delete is unimplemented: the session store exposes no delete, and the surface does not reach around that seam into its files. `/fork` covers the case that needs it — it branches into a new session and leaves the original alone.
334
- - Approvals and questions render inline and take the keyboard; a question batch is answered in order.
335
- - Styling is per element and overridable; see [Theme](#theme). Shipped defaults are emitted as 24-bit colour where the terminal advertises it and degraded to 256 or 16 colours otherwise, so a light or dark terminal still follows its own palette where it has one.
336
- - Tool text, model text, and file content are escaped before rendering, so a hostile result cannot inject terminal control sequences; the cost is that a literal tab shows as \x09.
587
+ - The prompt stash holds text only. It does not read Pi's `pi-stash` data, does not migrate an older key format, and keeps no pasted images: a draft larger than 1 MiB, or a bank larger than 16 MiB, is refused rather than stored — the cap is measured on the bytes the file will hold, escapes included, before anything is written, so a refusal cannot leave a bank that saves and then refuses to load. A corrupt bank is quarantined and reported, never repaired in place; a bank from a newer format is refused where it lies, so an older build cannot swallow a newer build's drafts.
588
+ - Approvals and questions render inline and take the keyboard; a question batch is answered in order, and a question that lists options can always be answered with free text on row `0`.
589
+ - Inside Herdr the pane reports its own state, and that report is the only thing that makes it an agent there: Herdr cannot start, resume, or prompt this surface, so launching and resuming stay with `dsh` itself (or a Herdr plugin that runs it).
590
+ - Styling is per element and overridable; see [Settings](#settings). Shipped defaults are emitted as 24-bit colour where the terminal advertises it and degraded to 256 or 16 colours otherwise, so a light or dark terminal still follows its own palette where it has one.
591
+ - Tool text, model text, and file content are escaped before rendering, so a hostile result cannot inject terminal control sequences; the cost is that a literal tab shows as \x09. A stashed draft is stripped of control and bidi characters instead, because it is restored into a live editor rather than drawn as text.
592
+ - Mermaid fences draw in assistant replies only, and only at the top level of one: a fence nested in a list, quoted inside another fence, or carried by a prompt, a thought, or a tool card stays source. Author `:::class` styling and diagram links are ignored — the renderer reports what each run is, and the theme decides how it looks.
593
+ - Prompt history is global to this machine, not per project: `$DSH_HOME/prompt-history.json` holds every submitted line, deduplicated exactly, and a file this build cannot parse is left untouched with writes refused so a newer format is never overwritten. Every write re-reads the file under a lock shared by sessions, so a second session's prompts are folded in rather than overwritten, and a lock whose holder stopped is reclaimed or reported instead of guessed at; control characters are spelled out before a prompt is stored. A multiline suggestion draws its first line with `↵` marking the fold. `history.ghost: false` keeps reverse search without the suggestion, `history.enabled: false` stops recording and offering it, and `NO_COLOR`/`--no-color` suppresses the ghost because text the reader cannot see but could still accept is worse than none.
337
594
 
338
595
  ## License
339
596
 
package/cordis.patch.yml CHANGED
@@ -97,6 +97,20 @@
97
97
  color: !!js ctx.tuiStartup.color
98
98
  bell: !!js ctx.tuiStartup.bell
99
99
 
100
+ # Advisory todo discipline for every agent this profile runs. The tool and
101
+ # its list already exist per session; this row only notices when a plan has
102
+ # aged and rides the next tool result with a reminder. It is host-plane on
103
+ # purpose: it registers no tool, provides no service, and never gates a
104
+ # call, so it needs no preset and no realm. `staleSteps` and the rest are
105
+ # documented in the README and overridable from a profile patch.
106
+ - id: tui-todo-guard
107
+ name: '@sagmans/dsh-tui/todo-guard'
108
+ config:
109
+ staleSteps: 6
110
+ missingListSteps: 12
111
+ maxRemindersPerTurn: 3
112
+ previewItems: 5
113
+
100
114
  # The roster of agent compositions, and the mode a session starts in when
101
115
  # nobody names one. PTC leads because a flagless run should reach every tool
102
116
  # through one program rather than one shell call at a time. A profile patch
@@ -1,4 +1,5 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
+ import type { PickerRow } from '../ui/picker.ts';
2
3
  /** The route a reader chose for the next step. */
3
4
  export interface ModelChoice {
4
5
  readonly provider: string;
@@ -10,6 +11,13 @@ export interface ProviderEntry {
10
11
  readonly id: string;
11
12
  readonly name: string;
12
13
  }
14
+ /** One route a configured provider advertises, as the picker lists it. */
15
+ export interface ModelRoute {
16
+ readonly provider: string;
17
+ readonly model: string;
18
+ /** The adapter's display name; the id stays the value a request names. */
19
+ readonly name: string;
20
+ }
13
21
  /** What a `/model` argument asks the surface to do. */
14
22
  export type ModelCommand = {
15
23
  readonly kind: 'current';
@@ -36,6 +44,24 @@ export type ModelCommand = {
36
44
  * id containing a slash as an effort the adapters in use do not mint.
37
45
  */
38
46
  export declare function parseModelArgument(argument: string, providers: readonly ProviderEntry[], current: ModelChoice | undefined): ModelCommand;
47
+ /** The picker's stable id for one route. */
48
+ export declare function modelRouteKey(route: {
49
+ readonly provider: string;
50
+ readonly model: string;
51
+ }): string;
52
+ /** Read a route key back; undefined for a string this surface never minted. */
53
+ export declare function readModelRouteKey(key: string): {
54
+ provider: string;
55
+ model: string;
56
+ } | undefined;
57
+ /**
58
+ * How one advertised route appears in the picker.
59
+ *
60
+ * The name leads because it is what a reader recognizes, and the id follows
61
+ * only when it says something the name does not; the route in force carries
62
+ * its effort, since that is the other half of the choice on screen.
63
+ */
64
+ export declare function describeModelRoute(route: ModelRoute, current: ModelChoice | undefined): PickerRow;
39
65
  /**
40
66
  * Own the reader's model choice for one session.
41
67
  *
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/agent/model.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAED,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,uDAAuD;AACvD,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,OAAO,EAAE,WAAW,GAAG,SAAS,GAC/B,YAAY,CAyBd;AAED;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkE;IAC5F,OAAO,CAAC,SAAS,CAA0B;IAE3C,sFAAsF;IACtF,OAAO,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAKhC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAUjC,OAAO,IAAI,WAAW,GAAG,SAAS;IAUlC;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKhC,4EAA4E;IAC5E,KAAK,IAAI,IAAI;IAKb,OAAO,IAAI,IAAI;CAIhB;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC1G,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAcD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG;IAChD,SAAS,IAAI,SAAS,aAAa,EAAE,CAAA;IACrC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAA;IAC5F,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAA;CAC5E,GAAG,SAAS,CA+BZ"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/agent/model.ts"],"names":[],"mappings":"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"}
@@ -40,6 +40,47 @@ export function parseModelArgument(argument, providers, current) {
40
40
  }
41
41
  return { kind: 'switch', choice: { provider: current.provider, model: trimmed } };
42
42
  }
43
+ /**
44
+ * The separator inside a picker's route key.
45
+ *
46
+ * A picker settles on a string, so the pair has to survive one round trip
47
+ * without a reading that guesses which half is which: a model id may contain
48
+ * the slash a `provider/model` reading would split on, and no route id can
49
+ * contain this character.
50
+ */
51
+ const ROUTE_KEY_SEPARATOR = '\u0000';
52
+ /** The picker's stable id for one route. */
53
+ export function modelRouteKey(route) {
54
+ return route.provider + ROUTE_KEY_SEPARATOR + route.model;
55
+ }
56
+ /** Read a route key back; undefined for a string this surface never minted. */
57
+ export function readModelRouteKey(key) {
58
+ const at = key.indexOf(ROUTE_KEY_SEPARATOR);
59
+ if (at <= 0 || at === key.length - 1)
60
+ return undefined;
61
+ return { provider: key.slice(0, at), model: key.slice(at + 1) };
62
+ }
63
+ /**
64
+ * How one advertised route appears in the picker.
65
+ *
66
+ * The name leads because it is what a reader recognizes, and the id follows
67
+ * only when it says something the name does not; the route in force carries
68
+ * its effort, since that is the other half of the choice on screen.
69
+ */
70
+ export function describeModelRoute(route, current) {
71
+ const chosen = current !== undefined && current.provider === route.provider && current.model === route.model;
72
+ const description = [
73
+ route.provider,
74
+ route.model === route.name ? undefined : route.model,
75
+ chosen ? 'current' : undefined,
76
+ chosen && current.reasoningEffort !== undefined ? `effort ${current.reasoningEffort}` : undefined,
77
+ ].filter(part => part !== undefined && part !== '').join(' · ');
78
+ return {
79
+ label: route.name,
80
+ description: description === '' ? undefined : description,
81
+ current: chosen,
82
+ };
83
+ }
43
84
  /**
44
85
  * Own the reader's model choice for one session.
45
86
  *
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/agent/model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/agent/model.ts"],"names":[],"mappings":"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"}