@aiwayds/dsh-tui-pi 2.18.2 → 2.19.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.
- package/README.md +5 -0
- package/README.zh-CN.md +5 -0
- package/lib/ask-user.d.ts +122 -3
- package/lib/ask-user.js +310 -16
- package/lib/ask-user.js.map +1 -1
- package/lib/index.js +5 -3
- package/lib/index.js.map +1 -1
- package/lib/subagent-viewer.d.ts +2 -4
- package/lib/subagent-viewer.js +2 -28
- package/lib/subagent-viewer.js.map +1 -1
- package/lib/theme-settings.d.ts +21 -0
- package/lib/theme-settings.js +36 -0
- package/lib/theme-settings.js.map +1 -1
- package/package.json +1 -1
- package/skills/dsh-tui-pi-config/SKILL.md +6 -3
package/README.md
CHANGED
|
@@ -149,8 +149,13 @@ dsh-tui:
|
|
|
149
149
|
resume: # /resume display filter — only HIDES picker rows, never deletes.
|
|
150
150
|
maxAgeDays: 30
|
|
151
151
|
minBytes: 1024
|
|
152
|
+
askUser: # ask_user_question auto-answer timeouts — the panel never waits forever.
|
|
153
|
+
idleMinutes: 5 # no-input window per question; <= 0 disables (DSH_TUI_ASK_USER_IDLE_MINUTES)
|
|
154
|
+
absoluteMinutes: 10 # hard cap per question even with input; <= 0 disables (DSH_TUI_ASK_USER_ABSOLUTE_MINUTES)
|
|
152
155
|
```
|
|
153
156
|
|
|
157
|
+
On a timeout the focused question is auto-answered with the **recommended option** (first in the list); plans are never auto-approved, a half-typed answer is committed, and every automatic pick is noted to the model in the answer. Details: [Ask User Question → Timeouts](docs/features/ask-user-question.md#timeouts--the-panel-never-waits-forever).
|
|
158
|
+
|
|
154
159
|
Key remaps live in `~/.dsh/keybindings.json` (keyboard section above); `DSH_TUI_COPY_ON_SELECT=0` keeps drag-selection visual-only.
|
|
155
160
|
|
|
156
161
|
The plugin ships a bundled skill (`dsh-tui-pi-config`): ask the agent to "configure the TUI" and the guide loads automatically — it collects your choices interactively via ask_user_question (theme, panel height, subagent concurrency) and writes the `dsh-tui:` section for you. The full key table and the `DSH_TUI_*` env var list live in `skills/dsh-tui-pi-config/SKILL.md`.
|
package/README.zh-CN.md
CHANGED
|
@@ -145,8 +145,13 @@ dsh-tui:
|
|
|
145
145
|
resume: # /resume 显示过滤器——只隐藏选择器行,从不删除。
|
|
146
146
|
maxAgeDays: 30
|
|
147
147
|
minBytes: 1024
|
|
148
|
+
askUser: # ask_user_question 自动应答超时——面板绝不无限等待。
|
|
149
|
+
idleMinutes: 5 # 每题无操作窗口;<= 0 关闭(DSH_TUI_ASK_USER_IDLE_MINUTES)
|
|
150
|
+
absoluteMinutes: 10 # 每题硬上限,有操作也生效;<= 0 关闭(DSH_TUI_ASK_USER_ABSOLUTE_MINUTES)
|
|
148
151
|
```
|
|
149
152
|
|
|
153
|
+
超时后,聚焦的未答题目自动选**推荐项**(列表第一项);计划审批绝不自动批准;打了一半的自由文本会被提交;每次自动作答都会在答案里向模型注明。详见 [Ask User Question → Timeouts](docs/features/ask-user-question.md#timeouts--the-panel-never-waits-forever)。
|
|
154
|
+
|
|
150
155
|
按键重映射见 `~/.dsh/keybindings.json`(上文键盘一节)。
|
|
151
156
|
|
|
152
157
|
插件内置了一个 skill(`dsh-tui-pi-config`):直接让 agent「帮我配置 TUI」,指南会自动加载——以 ask_user_question 问答方式逐项收集(主题、面板高度、子代理并发)并代写 `dsh-tui:` 段;全键表与 `DSH_TUI_*` 环境变量清单见 `skills/dsh-tui-pi-config/SKILL.md`。
|
package/lib/ask-user.d.ts
CHANGED
|
@@ -124,6 +124,34 @@ export declare const ASK_COLLAPSE_KEY: KeyId;
|
|
|
124
124
|
export declare const DECLINE_MESSAGE = "User declined to answer questions.";
|
|
125
125
|
/** Transient hint when Enter lands on an incomplete confirm/submit row. */
|
|
126
126
|
export declare const INCOMPLETE_HINT = "Answer every question first";
|
|
127
|
+
/** Default no-input window per focused question, in minutes: after this long
|
|
128
|
+
* without a single keypress the question is auto-answered (recommended
|
|
129
|
+
* option, plan-safe) and the panel moves on. */
|
|
130
|
+
export declare const ASK_USER_IDLE_MINUTES_DEFAULT = 5;
|
|
131
|
+
/** Default hard cap per focused question, in minutes: after this long the
|
|
132
|
+
* question is auto-answered EVEN IF the user keeps interacting — the panel
|
|
133
|
+
* must never hold a run hostage indefinitely (dual rule with the idle
|
|
134
|
+
* window; either firing resolves the question). */
|
|
135
|
+
export declare const ASK_USER_ABSOLUTE_MINUTES_DEFAULT = 10;
|
|
136
|
+
/** Env override for the no-input window (minutes; <= 0 disables the rule). */
|
|
137
|
+
export declare const ASK_USER_IDLE_ENV = "DSH_TUI_ASK_USER_IDLE_MINUTES";
|
|
138
|
+
/** Env override for the hard cap (minutes; <= 0 disables the rule). */
|
|
139
|
+
export declare const ASK_USER_ABSOLUTE_ENV = "DSH_TUI_ASK_USER_ABSOLUTE_MINUTES";
|
|
140
|
+
/** Note folded into the answer envelope's `custom` field when a question was
|
|
141
|
+
* auto-answered with the recommended option — the model reads the answer as
|
|
142
|
+
* a tool result, so an automatic pick is declared in-band (same spirit as
|
|
143
|
+
* DECLINE_MESSAGE), never silently passed off as a human choice. */
|
|
144
|
+
export declare const TIMEOUT_RECOMMENDED_NOTE = "Auto-answered after the no-input timeout: the recommended option was picked.";
|
|
145
|
+
/** Note for a plan-review question auto-answered by timeout: plans are NEVER
|
|
146
|
+
* auto-approved — the first non-approve option is picked instead. */
|
|
147
|
+
export declare const TIMEOUT_PLAN_DECLINED_NOTE = "Auto-answered after the no-input timeout: no user input, so the plan was NOT approved.";
|
|
148
|
+
/** Note for a question with no options at all — there is no recommended value
|
|
149
|
+
* to fall back to, so the answer carries the explanation only. */
|
|
150
|
+
export declare const TIMEOUT_NO_DEFAULT_NOTE = "Auto-answered after the no-input timeout: the question offers no default option to pick.";
|
|
151
|
+
/** Note appended when the timeout commits a half-typed sentinel buffer: the
|
|
152
|
+
* text is the user's own, but it never went through Enter, so the envelope
|
|
153
|
+
* says so. */
|
|
154
|
+
export declare const TIMEOUT_CUSTOM_NOTE = "Auto-committed after the no-input timeout: the text typed so far.";
|
|
127
155
|
/**
|
|
128
156
|
* Fallback visible body-line cap for the questions/review panes, used only
|
|
129
157
|
* when the terminal row count is unknown (fake TUIs in tests, exotic
|
|
@@ -286,6 +314,81 @@ export declare function needsConfirmRow(questions: readonly AskUserQuestionItem[
|
|
|
286
314
|
* question may want further toggles), and every question answered.
|
|
287
315
|
*/
|
|
288
316
|
export declare function canAutoSubmit(state: AskUserState): boolean;
|
|
317
|
+
/**
|
|
318
|
+
* Timeout rules for one open ask-user panel — the unit is ms internally,
|
|
319
|
+
* minutes in settings/env (human units, like retention's maxAgeDays).
|
|
320
|
+
* `0` disables that rule; both zero = the legacy wait-forever panel.
|
|
321
|
+
*/
|
|
322
|
+
export interface AskUserTimeouts {
|
|
323
|
+
/** No-input window per focused question. Any keypress restarts it. */
|
|
324
|
+
idleMs: number;
|
|
325
|
+
/** Hard cap per focused question, measured from when focus entered it —
|
|
326
|
+
* fires even under continuous input. */
|
|
327
|
+
absoluteMs: number;
|
|
328
|
+
}
|
|
329
|
+
/** Both rules off — the panel waits for the human exactly as before. */
|
|
330
|
+
export declare const ASK_USER_TIMEOUTS_DISABLED: AskUserTimeouts;
|
|
331
|
+
/** Raw user-layer `dsh-tui.askUser` section (handed over by the
|
|
332
|
+
* theme-settings reader): every field is `unknown` because a hand-edited
|
|
333
|
+
* settings.yaml can carry anything. */
|
|
334
|
+
export interface AskUserTimeoutSettings {
|
|
335
|
+
idleMinutes?: unknown;
|
|
336
|
+
absoluteMinutes?: unknown;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Resolve the ask-user timeout knobs through the repo's standard precedence
|
|
340
|
+
* chain — explicit settings.yaml values (`dsh-tui.askUser.*`) outrank the
|
|
341
|
+
* `DSH_TUI_ASK_USER_*` environment variables, which outrank the defaults
|
|
342
|
+
* (5 min idle / 10 min absolute). Invalid settings values emit one notice
|
|
343
|
+
* each (notice bridge) and fall to the next level; invalid env values fall
|
|
344
|
+
* back silently. Pure; `process.env` and the settings section are passed
|
|
345
|
+
* explicitly so tests can pin them.
|
|
346
|
+
*/
|
|
347
|
+
export declare function resolveAskUserTimeouts(settings?: AskUserTimeoutSettings, env?: Record<string, string | undefined>): AskUserTimeouts;
|
|
348
|
+
/**
|
|
349
|
+
* Earliest deadline at which the panel's timeout should fire, or null when
|
|
350
|
+
* no rule is armed. The idle deadline runs from `lastInputAt` (any
|
|
351
|
+
* keypress); the absolute deadline runs from `focusEnteredAt` (focus entry
|
|
352
|
+
* into the current question) and applies in BOTH phases — a review page
|
|
353
|
+
* entered late inherits its question's remaining budget, so the total wait
|
|
354
|
+
* per question stays bounded. Pure; the panel arms a timer on this.
|
|
355
|
+
*/
|
|
356
|
+
export declare function nextTimeoutDeadline(timeouts: AskUserTimeouts, now: number, focusEnteredAt: number, lastInputAt: number): number | null;
|
|
357
|
+
/**
|
|
358
|
+
* The timeout auto-answer for ONE unanswered question — what the panel
|
|
359
|
+
* writes into `perQuestion` when a timer fires on it. Priority:
|
|
360
|
+
*
|
|
361
|
+
* 1. a non-empty live sentinel buffer is committed as the custom answer
|
|
362
|
+
* (the panel's own ↑↓ arrow-exit semantics commit non-empty buffers, so
|
|
363
|
+
* typed-but-uncommitted text is honored, not discarded);
|
|
364
|
+
* 2. a `plan-review` question NEVER auto-approves — the first option that
|
|
365
|
+
* is not the intent's `approve` label is picked (decline by omission);
|
|
366
|
+
* 3. otherwise the FIRST option — the dsh tool contract declares the
|
|
367
|
+
* recommended choice by putting it first ("put it first and append
|
|
368
|
+
* '(Recommended)' to that label").
|
|
369
|
+
*
|
|
370
|
+
* The companion envelope note is returned alongside so the panel can fold
|
|
371
|
+
* it into the answer at settle time; the note never enters panel state
|
|
372
|
+
* (it would leak into the sentinel row / review rendering).
|
|
373
|
+
*/
|
|
374
|
+
export declare function timeoutAnswerFor(question: AskUserQuestionItem, liveBuffer: string | null | undefined): {
|
|
375
|
+
answer: PendingAnswer;
|
|
376
|
+
note?: string;
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* `buildAnswerEnvelope` with the timeout notes folded in: `notes` maps a
|
|
380
|
+
* question index to the note appended to that answer item's `custom` field
|
|
381
|
+
* (created when absent, concatenated when the answer already carries custom
|
|
382
|
+
* text — the buffer-commit case). The model reads the envelope as the tool
|
|
383
|
+
* result, so every automatic pick is declared in-band. Omitting `notes`
|
|
384
|
+
* reproduces `buildAnswerEnvelope` exactly.
|
|
385
|
+
*/
|
|
386
|
+
export declare function buildAnswerEnvelopeWithNotes(state: AskUserState, notes?: ReadonlyMap<number, string>): AskUserQuestionAnswer;
|
|
387
|
+
/**
|
|
388
|
+
* The idle countdown for the panel footer: `m:ss` until the next deadline,
|
|
389
|
+
* floored at 0:00. Pure so tests pin it without a clock.
|
|
390
|
+
*/
|
|
391
|
+
export declare function formatCountdown(msRemaining: number): string;
|
|
289
392
|
/**
|
|
290
393
|
* Row index to land on after a single-select answer on question `answeredQi`
|
|
291
394
|
* (option toggle or committed custom text): advance the tab focus to the
|
|
@@ -321,9 +424,9 @@ export declare function switchFocus(state: AskUserState, direction: 1 | -1): Ask
|
|
|
321
424
|
*/
|
|
322
425
|
export declare function toggleCollapse(state: AskUserState): AskUserState;
|
|
323
426
|
/** Render the questions pane (one focused question tab) as a flat table line list behind a scroll window. */
|
|
324
|
-
export declare function renderQuestionsView(theme: TuiTheme, state: AskUserState, width: number, maxVisible?: number): string[];
|
|
427
|
+
export declare function renderQuestionsView(theme: TuiTheme, state: AskUserState, width: number, maxVisible?: number, countdown?: string): string[];
|
|
325
428
|
/** Render the review page for multi-question overlays (scroll-windowed). */
|
|
326
|
-
export declare function renderReviewView(theme: TuiTheme, state: AskUserState, width: number, maxVisible?: number): string[];
|
|
429
|
+
export declare function renderReviewView(theme: TuiTheme, state: AskUserState, width: number, maxVisible?: number, countdown?: string): string[];
|
|
327
430
|
/**
|
|
328
431
|
* The ONE interior line of the folded (Ctrl+T) panel. The panel is a hard
|
|
329
432
|
* modal that owns the keyboard while it pends, so the strip keeps the
|
|
@@ -411,6 +514,13 @@ export interface AskUserPanelDeps {
|
|
|
411
514
|
setModalActive: (active: boolean) => void;
|
|
412
515
|
/** Injectable clock for tests; defaults to Date.now. */
|
|
413
516
|
now?: () => number;
|
|
517
|
+
/**
|
|
518
|
+
* Resolve the timeout rules for ONE ask (settings > env > defaults, read
|
|
519
|
+
* fresh per ask so a committed settings change applies to the next
|
|
520
|
+
* question without a reload). Absent = disabled — the panel waits for the
|
|
521
|
+
* human exactly as before (direct callers, tests, embedders).
|
|
522
|
+
*/
|
|
523
|
+
resolveTimeouts?: () => Promise<AskUserTimeouts>;
|
|
414
524
|
}
|
|
415
525
|
/** Result promise from `openAskUserPanel`. Declined carries the canonical decline envelope. */
|
|
416
526
|
export type AskUserResult = AskUserQuestionAnswer;
|
|
@@ -424,8 +534,17 @@ export type AskUserResult = AskUserQuestionAnswer;
|
|
|
424
534
|
* service already screens entry-time aborts, and a step aborted after this
|
|
425
535
|
* point discards the tool result anyway — resolving keeps the pending promise
|
|
426
536
|
* from ever hanging either way.
|
|
537
|
+
*
|
|
538
|
+
* `timeouts` arms the auto-answer rules (see {@link AskUserTimeouts}): the
|
|
539
|
+
* FOCUSED question's idle window and hard cap. A firing timer auto-answers
|
|
540
|
+
* the focused question (recommended option, plan-safe —
|
|
541
|
+
* {@link timeoutAnswerFor}), hops to the next unanswered question with fresh
|
|
542
|
+
* budgets, and — once nothing is left unanswered — settles the envelope
|
|
543
|
+
* directly (the review page exists for a human double-check an absent human
|
|
544
|
+
* cannot do). Defaults to disabled: callers opt in through the provider's
|
|
545
|
+
* `resolveTimeouts`.
|
|
427
546
|
*/
|
|
428
|
-
export declare function openAskUserPanel(deps: AskUserPanelDeps, questions: readonly AskUserQuestionItem[], signal?: AbortSignal): Promise<AskUserResult>;
|
|
547
|
+
export declare function openAskUserPanel(deps: AskUserPanelDeps, questions: readonly AskUserQuestionItem[], signal?: AbortSignal, timeouts?: AskUserTimeouts): Promise<AskUserResult>;
|
|
429
548
|
/**
|
|
430
549
|
* Wires the TUI into `ctx.userQuestions`. Call from inside `ctx.effect`.
|
|
431
550
|
* Two registration shapes, chosen by what the host exposes:
|