본문으로 건너뛰기
W

dsh-reply-language

waldemarpng/dsh-reply-language

🌐 A DeepSeek Harness plugin: pick the language beside the model selector and every reply — sessions, subagents, teammates, side chats — follows. Auto releases the pin.

설치

dsh plugin --profile web add github:waldemarpng/dsh-reply-language

README

dsh-reply-language

Pick the language. The model answers in it — in every session, every agent.

A reply-language selector for DeepSeek Harness: a globe pill in the composer, right next to the model picker. Choose a language and all replies — this conversation, subagents, teammates, side chats — are written in it. Auto removes the pin.

A globe pill next to the model selector, with the language menu open

license node dependencies

Quick start · How it works · Languages · Configuration · Privacy · Troubleshooting · Development


Highlights

  • 🌐 One pill, global effect — the choice applies to the whole workspace: main sessions, subagents, teammates, and side chats run through the same hook.
  • 🧩 Native composer slot — registered in the app's own slot system (conversation.input.right), so it sits in the same control row as the model selector and inherits the design tokens, keyboard focus, and dark/light theme.
  • 🧹 Clean transcripts — one compact reply-language inject row per language, deduplicated against the session surface and re-affirmed after compaction. It is never re-injected on every step.
  • 🎯 16 languages + Auto — English, Russian, German, French, Spanish, Italian, Portuguese, Ukrainian, Turkish, Polish, Dutch, Simplified Chinese, Japanese, Korean, Hindi, Arabic.
  • 🖼️ Viewport-safe menu — the dropdown is portaled to document.body and clamped to the viewport, so it never slips off-screen or jumps with the composer.
  • 🔐 Nothing leaves the machine — the pill talks only to the local DSH webserver; the instruction is applied to messages the host already sends to your model provider.
  • ⚙️ Live config — the config file is re-read on every turn; no restart, no reload.
  • 🧩 Zero dependencies — no install scripts, no native modules, no npm install.

Quick start

dsh plugin --profile desktop add "link:/abs/path/to/dsh-reply-language"

Restart DeepSeek Harness once (the host half mounts at startup), then hard-refresh the browser (Cmd/Ctrl+Shift+R). A globe pill appears left of the model selector.

Install straight from GitHub:

dsh plugin --profile desktop add "github:waldemarpng/dsh-reply-language"

Pin an exact revision by appending a git ref, for example #v0.1.0. For local development, clone the repository and use the link: form above.

How it works

flowchart LR
    A[Globe pill in the composer] --> B[POST /dsh-reply-language/config]
    B --> C[~/.dsh/.dsh-reply-language.json]
    C --> D{agent/pre-step}
    D -->|language not yet pinned| E[Inject one reply-language message]
    D -->|already pinned this session| F[Skip]
    E --> G[Model answers in the pinned language]
  • The client half registers a pill into the conversation.input.right composer slot. The menu is portaled to document.body, positioned by measuring the real menu box, and clamped into an 8px viewport margin above or below the pill depending on available space.
  • The host half listens on agent/pre-step and injects one compact instruction message (source: reply-language): write every user-visible reply in X; keep code, identifiers, commands, paths and quoted text unchanged; an explicit per-message request still wins.
  • The instruction is injected once per language per session, deduplicated against the session surface and re-affirmed after compaction. Changing the language injects a new instruction from the next turn; the old row stays in history as a harmless superseded entry.

Languages

PillLanguage
AutoFollow the conversation — no pin.
ENEnglish
RUРусский
DEDeutsch
FRFrançais
ESEspañol
ITItaliano
PTPortuguês
UKУкраїнська
TRTürkçe
PLPolski
NLNederlands
ZH中文(简体)
JA日本語
KO한국어
HIहिन्दी
ARالعربية

Configuration

Config lives in ~/.dsh/.dsh-reply-language.json (or $DSH_HOME/…), written by the pill itself and re-read on every turn.

{
  "language": "ru",
  "enabled": true
}
FieldMeaning
languageauto (default) or a supported id: en, ru, de, fr, es, it, pt, uk, tr, pl, nl, zh-hans, ja, ko, hi, ar.
enabledfalse disables enforcement without losing the choice.

A broken config never crashes the host: the HTTP route reports the error and the agent hook quietly behaves like auto.

Routes

RouteMethodPurpose
/dsh-reply-language/configGETRead the current config.
/dsh-reply-language/configPOSTUpdate language / enabled (atomic write, mode 0600).

Local routes are fenced: cross-site requests, opaque null origins, and origin/host mismatches are rejected; POSTs without browser fetch metadata are only accepted from loopback.

Privacy

Nothing leaves the machine: the pill talks only to the local DSH webserver, and the instruction is applied to messages the host already sends to your model provider. No telemetry, no third-party service.

Troubleshooting

The pill never appears

Restart DeepSeek Harness after installing (the host half mounts at startup), then hard-refresh the browser (Cmd/Ctrl+Shift+R). Check the browser console for /dsh-reply-language/client.js errors.

The pill shows "— / host route unavailable"

The host half is not mounted, so the config route is missing. Re-run the install, restart the app once, then hard-refresh.

The menu opens off-screen or jumps

The menu is portaled to document.body and clamped to the viewport. If it still misbehaves, hard-refresh to make sure the latest bundle is loaded — the host serves each build under a new revision hash.

The language change takes effect only on the next turn

By design. The instruction is injected at the next agent/pre-step, so the very first reply after switching still follows the previous language.

The model still answers in another language

A later user message asking for a specific language overrides the pin for that message — that is intentional. Otherwise check that enabled is true and the config file parses: cat ~/.dsh/.dsh-reply-language.json.

Resource lifecycle

The plugin leaks nothing. The config route is disposed with its webserver scope, and the agent/pre-step hook and route registration are tied to the plugin fiber. On the client, every document/window listener is removed in its effect cleanup, the portaled menu is unmounted with React, and the injected <style> is deduplicated. No timers, no intervals, no unbounded caches.

Development

Node.js ≥ 20, zero dependencies, no build step.

CommandWhat it does
npm run checkSyntax-check every module.
npm testOffline tests: config, instruction dedupe, routes, client registration, menu placement.
npm run guardFails on comments or non-ASCII characters in code (localized label files exempt).
npm run smokeLive check against a running host: DSH_URL env picks the server, defaults to http://127.0.0.1:62498.

License

MIT — see LICENSE.

관련 플러그인