DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

xiaoshenming /

xiaoshenming/dsh-next-hint

Verified

Ghost-text next-prompt suggestions in the DeepSeek Harness composer — Tab to accept, never auto-sends. 回合结束后在输入框里推荐下一句话,按 Tab 采纳。

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@6c42a6d5

dsh-next-hint

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 it

Why install it

  • It knows what you were doing. One decoupled auxiliary request reads the last few turns of this session — the file you touched, the test that went red, where the work stopped — instead of a template like "continue?".
  • It never touches your Enter key. Accepting inserts ordinary text into the draft. Ctrl+Z undoes it; sending stays your decision. There is no send path in the code at all.
  • It stays quiet. No route, timeout, rate limit, malformed output, or a plain "not today" all end as no ghost and no error surface — at most one warn in the log.
  • It does not ship your conversation anywhere. The browser posts a sessionId; the host reads the transcript inside its own process, writes it nowhere, logs no text, and keeps it on this machine.

Install

DSH Desktop: Settings → Plugins → add a git package

github:xiaoshenming/dsh-next-hint#main

The 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. Update from the plugin page, or:

cd ~/.dsh/profiles/desktop && pnpm update dsh-next-hint

If the new version does not show up after an update, restart the app once.

What you get

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.

Configuration

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

How it works

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:

  • Decide in the browser, generate in the host. Only the browser knows whether the draft is empty, whether you are typing, and which session is on screen; asking the host to guess gets it wrong twice.
  • One request/response. No SSE, no long-lived connections, no server-side cache. Cancelling is AbortController.abort().
  • Official slot, own pixels. The component mounts in 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.
  • The auxiliary call follows the session-title recipe: its own system instruction, its own timeout, the cheapest reasoning level, and a failure that cannot touch the main conversation.

Related work

Two neighbouring plugins exist; neither is the same tool, but both can paint on the same composer:

  • Same idea: Composer predictions (dsh-composer-prediction). Both turn "a turn settled" into one auxiliary request and a ghost line; the seams differ (they mount the overlay slot, ship their own settings page, cache per session and dismiss with Esc; this one uses the official dock slot and a stateless host POST). Do not install both — two overlapping ghosts and two auxiliary calls per turn.
  • Complementary: dsh-input-assist completes 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.

Compatibility

  • Measured on DSH Desktop 0.2.1-rc.2 (Windows) and the same version's web profile (macOS). The host half needs ctx.llm, ctx.sessions, and ctx.webServer.register; the browser half needs the conversation.composer.dock slot and the composer input facade.
  • Still 0.x: if upstream moves those seams, the ghost breaks first. Its position relies on the editor's [data-input-scroll] structure; a missing anchor means no ghost, silently.
  • Requests from outside loopback are refused by the route.
  • If DSH ships this natively, this plugin can be disabled cleanly — nothing here assumes it is the only writer of the composer.

Development

node --test      # 49 tests, node:test only, zero runtime dependencies

Pure 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.

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 6c42a6d51adf

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout