Composer prompt refinement for DSH: rewrites a rough draft into a structured, unambiguous prompt with one-click undo, reference-chip protection, three model sources (composer / account / any OpenAI-compatible endpoint) and disconnect-aware cancellation.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-prompt-optimization
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:KukaNo996/dsh-prompt-optimization
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
中文 | English
Turn that one-line "just sort out my weekly report" into a professional prompt with a clear task, stated purpose, no ambiguity, a sensible plan, and precise acceptance criteria — in one click, and undo it just as easily.
This is a DeepSeek Harness (DSH) Web plugin. The Optimize prompt button sits in the composer toolbar right next to the model selector. The rewrite replaces the draft in place and can be reverted at any time. A dedicated settings page configures the model source, API key, and optional multi-candidate evaluation, supporting both online and local OpenAI-compatible endpoints.
Why
Natural-language requests are usually vague: no context, no scope, no acceptance criteria, so the model has to guess. This plugin moves that guesswork into an explicit rewrite:
- Goals: clear task / clear purpose / unambiguous / well structured / sensible plan / precise objective
- Principles: stay faithful (never invent requirements or facts); when information is missing, mark an assumption or list at most three blocking questions — never answer with a question instead of a prompt
- Adaptive depth: short requests get a short prompt; complex ones expand into Objective / Background / Tasks / Constraints / Steps / Output format / Acceptance criteria
Features
| Feature | Description |
|---|---|
| One-click optimize | Calls the model and writes the rewritten prompt back into the draft |
| One-click undo | The button becomes Undo; click to restore the original, then optimize again |
| Multi-candidate evaluation | Generate 2–5 drafts in parallel and let a judge model pick the best one; token cost is stated up front |
| Configurable model source | Follow composer (default), Follow the app or Custom model (any OpenAI-compatible endpoint) |
| Local models | Requests are made by the host side, so browser CORS never blocks Ollama / LM Studio / vLLM |
| Show hint text | Off by default (icon only); when on, both "Optimize prompt" and "Undo" labels are shown |
| Empty-draft guidance | Clicking optimize on an empty draft types a hint into the composer, holds it for 2 seconds, then erases it character by character; typing cancels it immediately |
| Classified rewriting | The original is classified into one of six types first — knowledge, coding, writing, analysis, process, conversion — and the type decides what to ask |
| Question decomposition | A question like "what is X" is never answered; it is decomposed into at least 3 analysis dimensions |
| Five length tiers | Tier is picked from the original length and type: tiny / short / standard / detailed / full, never mixed within one rewrite |
| Cancellable optimization | Click the spinner while the optimization runs to cancel it; the draft is restored |
| Maximum wait | Longest wait for one optimization, 15 seconds by default and 10 seconds at minimum; it aborts on its own |
| Optimize with context (Beta) | Optionally compress the current conversation first and rewrite against it, so the result fits your usual phrasing; off by default |
| Reference guard | Disabled when the draft contains file/session reference chips, which a plain-text rewrite would drop |
| Failure-safe | If the draft changes during the request the result is not applied; in-flight work aborts on page close or session switch |
| Update check | Check for a newer version from the settings page with a configurable update source (npm registry / GitHub Releases / any mirror); reads the version only and installs nothing |
The UI and dictionaries ship in Simplified Chinese, Traditional Chinese, Japanese and English.
Installation
Option 1 — Plugin manager (recommended)
Open Settings → Plugins in DSH, or install by package coordinate:
plugin_manager install_bundle target=dsh-prompt-optimization
For a faster registry inside mainland China:
plugin_manager install_bundle target=dsh-prompt-optimization registry=https://registry.npmmirror.com
Pin an exact version when you need one, e.g. target=dsh-prompt-optimization@1.1.0.
Option 2 — From source
git clone https://github.com/KukaNo996/dsh-prompt-optimization
plugin_manager install_bundle target=<absolute path of the clone>
After installation the plugin row appears under Settings → Plugins, and its page under Settings → Prompt Refine.
Quick start
- Type a request into the composer, e.g.
help me sort out the weekly report - Click Optimize prompt in the toolbar
- Review the result — send it, or click Undo
- If the model is unavailable, open Settings → Prompt Refine and pick a model source
Settings
| Setting | Purpose | Default |
|---|---|---|
| Model source | Follow composer, Follow the app or Custom model | Follow composer |
| API base URL | OpenAI-compatible endpoint in custom mode, e.g. http://127.0.0.1:11434/v1 |
— |
| Model | Required in custom mode; empty in follow mode means automatic | — |
| Provider (optional) | Overrides the auto-resolved provider in follow mode | automatic |
| API key | Key for the custom endpoint; local models may leave it empty | — |
| Temperature | 0–2, lower is steadier | 0.3 |
| Max output tokens | 0 leaves it to the model default | 0 |
| Refinement instruction | Custom system prompt; empty uses the built-in one | built-in |
| Multi-candidate evaluation | Toggle plus candidate count (2–5) | off / 1 |
| Show hint text | When on the control shows the "Optimize prompt" and "Undo" labels; when off it shows icons only | off (icon only) |
| Update source | Address used by the update check; empty tries the built-in order | automatic |
| Optimize with context (Beta) | Compress the conversation first, then rewrite against it | off |
| Maximum wait | Longest wait for one optimization, covering compression, candidate generation and judging | 15 seconds |
Use Test connection to validate the current settings. Incomplete settings still save; the optimization call then returns a readable error instead of failing silently.
Cancelling and the wait budget
While an optimization runs, clicking the spinner cancels it and the draft is restored. The cancel ends the client request and the host-side model call, so the model does not keep generating in the background.
The maximum wait limits how long one optimization may take, 15 seconds by default and 10 seconds at minimum. It covers the whole operation — context compression, candidate generation and judging — not just the last model call. When it runs out the plugin stops and says so.
Classification
The original is classified before anything is written, and the type decides what the prompt should ask:
| Type | Typical phrasings | What the rewrite spells out |
|---|---|---|
| Knowledge | what is, why, difference, principle | Several analysis dimensions, answered systematically |
| Coding | write, fix, refactor, error, performance | Inputs/outputs, language and framework, constraints, edge cases, acceptance |
| Writing | copy, naming, email, script, translation | Audience, purpose, tone, length, taboos |
| Analysis | compare, choose, evaluate, worth it | Candidates, evaluation dimensions, conclusion form, criteria |
| Process | how to, getting started, steps | Phases, per-phase output, dependencies, checkpoints |
| Conversion | summarise, shorten, rewrite, format | Target shape, what to keep, what to drop |
Questions are never answered
When you ask "what is a closure" or "React or Vue", the plugin never answers in an assistant voice. It rewrites your question into a prompt that another AI can answer:
- The question must be split into at least 3 analysis dimensions; a polished one-line question does not count;
- Six dimensions are available: definition, mechanism, taxonomy, applications, limits, practice;
- The reader level, depth and whether sources are needed are stated explicitly;
- Knowledge questions are written at the "detailed" tier even when the original is five characters long.
Five length tiers
Length is decided by the original length plus the question type, so a short request is never expanded into a wall of text:
| Tier | Original | Output |
|---|---|---|
| Tiny | under ~20 characters | 1–2 sentences; no headings, numbering or background |
| Short | 20–50 characters, one ask | 2–4 sentences or up to 3 short requirements |
| Standard | 50–150 characters, or 2–3 asks | 4–8 lines, at most 1–2 small headings |
| Detailed | 150–400 characters, or a type that needs a systematic answer | Structured sections: goal / dimensions / constraints / output shape / acceptance |
| Full | over 400 characters, or several dependent asks | The full structure with background, steps and checkpoints |
Tiers are never mixed within one rewrite. Shorter is better by one tier than the original justifies.
Optimizing with context (Beta)
The Optimize with context (Beta) switch is off by default. When on, the plugin:
- reads the messages you actually typed in this session (the host-injected runtime context does not count);
- asks the model to compress them into a short brief of your phrasing, topics and exclusions;
- rewrites the prompt against that brief.
Only the compressed brief is sent with the rewrite; the raw transcript never is. Compression costs one extra model call, so the token usage goes up; if it fails the rewrite falls back to the normal path.
Checking for updates
The settings page ends with a Check for updates block. It reports whether a newer version exists — it reads the version only, downloads and installs nothing, and never rewrites the settings file.
The update source accepts three forms:
| Form | Example |
|---|---|
| Built-in alias | npm, npmmirror, github |
| npm registry package URL | https://registry.npmmirror.com/dsh-prompt-optimization |
| GitHub Releases API URL | https://api.github.com/repos/KukaNo996/dsh-prompt-optimization/releases/latest |
When left empty the check tries npm registry → npmmirror → GitHub Releases and returns on the first success.
- A custom source is interpreted by its payload shape (
dist-tags.latestortag_name), so mirrors and proxies work regardless of hostname. - Each source times out after 10 seconds and the check falls back to the next one; if all fail, every reason is listed.
- Version comparison follows semver precedence, including prerelease ordering.
The three model sources
| Mode | Model used | When it is unavailable |
|---|---|---|
| Follow composer (default) | The model currently picked at the bottom right of the input box | Fails; it never switches to another model |
| Follow the app | See the resolution order below | Falls back step by step |
| Custom model | The OpenAI-compatible endpoint you configured | Fails with a hint about the missing URL or model |
'Follow composer' never falls back: picking it means you want the optimization to run on the very model you are talking to, so swapping in another one silently would defeat the point. When the session has no recognisable selection yet, the plugin says so instead of deciding for you.
Model resolution (follow the app)
- Provider + model explicitly set on the settings page
- The model used by the session's most recent request (i.e. the model selector)
- The app default model (Settings → Models)
- Fallback
deepseek-account/deepseek-flash
Requirements
- DeepSeek Harness 0.2.1 or newer (uses the
conversation.input.rightandsettings.sectionslots plusinputActions.setDraft) - Follow mode needs a signed-in DeepSeek account or a configured model; otherwise use a custom endpoint
Privacy & security
- API key storage: stored in the host credential service when available (
keyStorage: credential);settings.jsononly records whether a key exists. Without that service the key falls back to$DSH_HOME/dsh-prompt-optimization/settings.jsonwith mode0600. Plaintext keys from older versions are migrated automatically and removed. - Browser: only ever receives a mask such as
sk-••••1234; no route returns the plaintext. - Endpoint protection: every route except
/statusrequires a per-process random ticket (fetched from/status) and validatesOrigin; model routes are rate limited to 10 calls/minute per source. - Where data goes: the draft text is sent to the configured model endpoint (the app's own provider in follow mode). Do not optimize sensitive drafts against an untrusted third-party endpoint.
- Cancellation: closing the page, switching sessions, or clicking again aborts the request immediately; the host side answers 499 and stops the call.
Implementation
Host half (index.js)
Registers a same-origin prefix route /dsh-prompt-optimization/*:
| Route | Method | Purpose |
|---|---|---|
/status |
GET | Health check plus the request ticket |
/settings |
GET / POST | Read / save settings (the key is write-only) |
/optimize |
POST | Run the rewrite, including multi-candidate evaluation |
/test |
POST | Test the connection |
/update-check |
GET / POST | Check for updates (outside the model call budget) |
- Follow mode uses the DSH
llmservice (account auth and retry built in); custom mode calls an OpenAI-compatible/chat/completionsfrom the Host - Evaluation: with
candidates > 1it generates N drafts in parallel, then asks the judge once for an index; if the judge fails or returns no valid index it falls back to a deterministic score (structure + numeric/identifier retention + length ratio) - Cancellation:
req.on('close')produces an AbortSignal forwarded tollm.stream/fetch - Update check: an HTTPS GET to the update source (10s timeout, 2 MB cap) that reads a npm packument or a GitHub release by payload shape and compares versions with semver rules; read-only, it never writes the settings file
Client half (client.js)
Registers two slots through window.__ModuleLoader__.load:
conversation.input.right— the optimize / undo control and its error bubblesettings.section— the settings page
Drafts are read and written through the injected inputActions / useInput standard props; the DOM is never touched. Optimization is disabled while occurrences is non-empty. Styling uses only --dsw-alias-* theme tokens.
Package contents
Only index.js, client.js, cordis.patch.yml, locale/*.json, and icon.svg are published; tests and docs stay in the repository.
Development
node --check index.js
node --check client.js
node .verify/host-test.mjs # Host: routes, ticket/rate limit, key storage, cancellation, evaluation (46 checks)
node .verify/client-test.mjs # Client: module factory and slot registrations (14 checks)
node .verify/hint-animation-test.mjs # Empty-draft guidance: type / hold / erase and interruption (31 checks)
node .verify/icon-test.mjs # Icon structure contract (16 checks)
node .verify/locale-test.mjs # Locale resolution and key parity (36 checks)
node .verify/translate-sim.mjs # Actual output per language (14 checks)
node .verify/preamble-test.mjs # Preambles stripped, real prompts kept (12 checks)
node .verify/rename-migration-test.mjs # Rename migration: settings and key survive (9 checks)
node .verify/update-test.mjs # Update check: source parsing, semver, timeout, auth (37 checks)
node .verify/composer-mode-test.mjs # Follow composer mode (19 checks)
node .verify/disconnect-test.mjs # A client disconnect must reach the Host (5 checks)
node .verify/wait-budget-test.mjs # Wait budget and cancellation (34 checks)
python3 .verify/icon-weight.py # Icon visual balance from a real 16px render
Or run everything CI runs:
npm run verify
Committing
This directory is a complete Git repository with origin at https://github.com/KukaNo996/dsh-prompt-optimization and default branch main.
git status
git add -A -- . ':!.workbuddy'
git commit -m "fix: describe the change"
git push origin main
Local proxy: this repository sets
http.proxy = http://127.0.0.1:7897. For a different port:git config --local http.proxy http://127.0.0.1:<port> git config --local https.proxy http://127.0.0.1:<port>Once direct access works:
git config --local --unset http.proxyand--unset https.proxy.Credentials: Git Credential Manager already holds the GitHub account; if it expires, follow the prompt from
git push.Identity and line endings: repository-local
user.name=KukaNo996,user.email=1274666494@qq.com,core.autocrlf=false..workbuddy/,*.tgz, andnode_modules/are ignored.
FAQ
The button does nothing, or reports that no model is available. No account signed in and no model configured. Open Settings → Prompt Refine, switch to Custom model, fill in the API base URL and model, then click Test connection.
Why is the button greyed out after I typed? The draft contains file or session reference chips; a plain-text rewrite would drop them. Remove the references to continue.
Is multi-candidate evaluation worth it?
Yes when the result goes to someone else. N candidates cost roughly N + 1× the tokens of a single pass and take longer.
The rewrite expands too much. The built-in instruction already asks for length to match the source. You can still tune the Refinement instruction in settings — for example set an explicit word limit.
Known limitations
- Optimization and undo rewrite the draft as plain text; with reference chips the button is disabled (undo also restores text only)
- "The model selected in the composer" comes from the session's last request; right after switching models and before sending, the default model is used
- Custom endpoints must speak the OpenAI-compatible
/chat/completionsAPI - Multi-candidate evaluation costs about
N + 1× the tokens — best for quality-focused, latency-tolerant use - Verified in the Web GUI only; the standalone Electron window is untested
License
MIT © 2026 KukaNo996
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 9262
Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.
zhu1090093659/dsh-web#packages/dsh-web-all★ 9262
Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 5677
A fixed-corner whale widget for the DSH web GUI — balance, today's usage and per-turn cost with peak/off-peak pricing, editable balance-alert and daily-budget bubbles, a module-based custom bubble queue with A/B weighted choices and random lines or images, 30+ vendor templates (OpenAI, OpenRouter, Kimi, SiliconFlow, Ark, Zhipu, MiniMax and more) with per-model balance and subscription quota, plus task-end sound, imported audio, custom roles and a resource manager. Local-only, no telemetry.
ccch1mneyyy/dsh-TUI★ 5125
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
omdsh-dev/DSH-better-sidebar★ 4413
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1491
Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.