dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Ghost-text next-prompt suggestions in the DeepSeek Harness composer — Tab to accept, never auto-sends. 回合结束后在输入框里推荐下一句话,按 Tab 采纳。
When a turn ends, the composer suggests the next message by itself.
Tab accepts, nothing is ever sent.
A DeepSeek Harness plugin that brings Claude Code–style prompt suggestions to DSH — as ghost text inside your own composer: visible, undoable, and never on the return key.
┌────────────────────────────────────────────────────────────┐
│ fix the two failing cases as well Tab accept │ ← ghost, appears on settle
└────────────────────────────────────────────────────────────┘
↑ only while the draft is empty; typing dismisses itlast few turns of *this* session — the file you touched, the test that went red, where the work stopped — instead of a template like "continue?".
draft. Ctrl+Z undoes it; sending stays your decision. There is no send path in the code at all.
"not today" all end as no ghost and no error surface — at most one warn in the log.
sessionId; the host reads the transcript inside its own process, writes it nowhere, logs no text, and keeps it on this machine.
DSH Desktop: Settings → Plugins → add a git package
github:xiaoshenming/dsh-next-hint#mainThe desktop profile is owned by the app — dsh plugin --profile desktop … is refused by the CLI. For a self-managed profile such as web:
dsh plugin --profile web add "github:xiaoshenming/dsh-next-hint#main"Restart the app once — plugins join the roster at boot.
Updating: the plugin page's update check only compares npm-registry sources; a
github: install is skipped and always reads as "latest". For a GitHub install useone of these, then restart the app:
```bash
cd ~/.dsh/profiles/desktop && pnpm update dsh-next-hint
`or uninstall it on the plugin page and add github:xiaoshenming/dsh-next-hint#mainagain. To use the in-app update button, install the plugin from npm once it is published.
Moment · What happens A turn settles, draft is empty · One grey line at the composer's first line, Tab accept on the right Tab with focus in the composer · The sentence lands in the draft, caret at its end, not sent You start typing · The ghost disappears You switch sessions · The ghost is dropped Two declines / timeout / no route · Nothing at all — the correct outcome
A model that answers "nothing to suggest" is asked once more with a stricter instruction before the plugin gives up: one refusal never blanks the whole turn.
Tab only fires when no candidate menu owns it: with the / or @ menu open, Tab still belongs to the menu.
Settings → Plugins → dsh-next-hint. Every key has a default; nothing is required.
Key · Default · Meaning enabled · true · Master switch provider / model · empty · Empty follows the session's last route; set both or neither locale · auto · Language of the suggestion maxTurns · 3 · Exchanges sampled maxInputBytes · 8000 · Input budget for the auxiliary request maxOutputTokens · 128 · Output token cap maxHintChars · 48 · Hint length cap; a paragraph is dropped, a long sentence truncated timeoutMs · 15000 · Per-call timeout perSessionCooldownMs · 0 · Per-session cooldown; default suggests every turn
A dual-face cordis plugin, the same shape as dsh-session-surgeon:
browser session.running true → false, draft empty
└─ POST /api/next-hint { sessionId } ← id only, no transcript
host loopback + origin check → single flight + cooldown
└─ ctx.sessions.get(id).deriveMessages() ← recent turns
└─ ctx.llm.prepareCall(...) ← one decoupled call, cheapest effort
└─ 200 { hint } / 204 (silent, with x-next-hint-reason)
browser paint the ghost on line one → Tab → input.actions.captureInsertion() + insertText()Deliberate choices:
the draft is empty, whether you are typing, and which session is on screen; asking the host to guess gets it wrong twice.
cache. Cancelling is AbortController.abort().
conversation.composer.dock; the ghost node is ours because the host exposes no inline-hint write path. Unmounting removes the node, the listeners, and the in-flight request.
instruction, its own timeout, the cheapest reasoning level, and a failure that cannot touch the main conversation.
Four same-kind plugins now exist, plus one complementary tool; prefer one same-kind:
dropped on a session switch.
dsh-composer-prediction):same idea; overlay slot, per-session cache, Esc, settings page. From 0.7.7 it detects this plugin and pauses itself (this plugin wins).
dsh-composer-predictions):same idea plus draft continuation; Alt+↑/↓ candidate cycling, Esc, owner admission; needs DSH ≥0.2.1-rc.3.
dsh-reply-suggestions):same idea; Enter-to-fill, Esc, usage/cost stats; built for rc.2.
dsh-input-assistcompletes *the sentence you are typing* and checks typos; this plugin suggests *the next message after a turn*. The only overlap is an idle empty composer — if you see two ghosts there, turn one of them off.
ctx.llm, ctx.sessions, and ctx.webServer.register; the browser half needs the conversation.composer.dock slot and the composer input facade.
0.x: if upstream moves those seams, the ghost breaks first. Its positionrelies on the editor's [data-input-scroll] structure; a missing anchor means no ghost, silently.
assumes it is the only writer of the composer.
node --test # 49 tests, node:test only, zero runtime dependenciesPure logic lives in src/ (no cordis, no official packages, no DOM); seams live in plugin/. Design decisions and the evidence behind them are in docs/, from the line-by-line sources in RESEARCH.md to the data flow and failure modes in ARCHITECTURE.md.
MIT