Better Experience
English | 简体中文
A DeepSeek Harness plugin that rewrites the prompt you are about to send — Standard or Professional, with scene-aware routing — and keeps your account balance and time-of-use pricing in view at the sidebar foot.
Community plugin. Independently maintained; not built, reviewed, or supported by DeepSeek. A plugin runs with the host's own permissions, so read the source before installing it into a profile that holds real data.
| Package | @unknowssq/better-experience — MIT; the first npm release is pending |
| Surface | Web client (dsh.client.platform: "web"): the web profile and the Desktop app |
| Requires | dsh >=0.2.1-rc.5 (engines.dsh) |
| Install | dsh plugin --profile web add "@unknowssq/better-experience@link:<path to this checkout>" |
| Exposes | one host command /be-optimize, two HTTP routes, five client slots |
In the package: index.js holds the Config schema and the command; client.js holds all five slot contributions as a single window.__ModuleLoader__ bundle. Verified on dsh 0.2.1-rc.5 (Windows), last checked 2026-10-31.
Table of contents
- Features
- Requirements
- Installation
- Quick start
- Configuration
- Usage
- Commands and routes
- How a profile loads this plugin
- Architecture
- Development
- Permissions and data
- Compatibility
- Distribution and listing status
- Contributing
- License
- Changelog
Features
Prompt optimization
- One-click rewrite — the sparkle button left of Send (
conversation.input.activity) rewrites the current draft in place: only the clean prompt reaches your editor, and nothing else is appended to it. - Manual only — nothing rewrites the draft in the background and nothing delays a send. The draft changes only when you press the button; Enter always submits exactly what is in the composer.
- Two fixed modes — Standard keeps the rewrite concise while clarifying the request; Professional models task complexity and adds execution and acceptance detail when needed.
- Scene routing —
professionaleither classifies the scene itself (auto) or is pinned to one of eight scenes (coding,writing,research,data,creative,business,agent,general).codingadditionally enables the implementation sub-scene rules. - Output language — follow the draft's language, or pin Chinese / English.
Undo without leaving the composer
- The button becomes undo — right after a rewrite the same button switches to an undo icon. One click puts the pre-optimization draft back verbatim, and the sparkle returns.
- A hand edit retires the slot — as soon as you change the draft yourself, the undo state is dropped, so the button can never restore text over edits it did not make.
- Typing keys stay the host's — the plugin registers no
Ctrl+Z/Ctrl+Ybinding. Undo and redo inside the composer keep the host editor's own behaviour.
Awareness while you write
- Balance and price card — mounted at
sidebar.footer.action: current balance, a peak / off-peak chip counting down to the next switch, and the cache-hit input price for Flash and V4 Pro at that period. It polls every 60 s, refreshes on focus or tab return, keeps the last snapshot as stale instead of flashing an error, and collapses to a wallet icon with a status dot in the rail. - Draft meter — mounted at
conversation.input.left: estimated tokens for the live draft, their cost at the current period, and three dots that check goal / constraints / acceptance locally. No tokenizer, no model call, no network. - Project templates — read from
.agents/prompts/*.md; typing/tpl:<name>alone and pressing Enter inserts the skeleton without sending, and a pinned template is passed to the optimizer as a structural reference.
Requirements
| Requirement | Detail |
|---|---|
| dsh | >=0.2.1-rc.5 (engines.dsh in package.json) |
| Client bundle | The Web client (@deepseek-ai/dsh-web-app). A host-only profile loads the plugin but has no UI. |
| Default model | /be-optimize calls the currently selected model through ctx.llm; with no selection it answers 「没有可用模型,请先配置模型」. |
| Optional services | fs and webServer. Both are probed, not injected — without them the template route is simply not registered. |
| Node / pnpm | Whatever the DSH runtime ships. The plugin itself has one runtime dependency, @deepseek-ai/schemastery. |
Installation
The package is named @unknowssq/better-experience and is prepared for npm, but 0.1.0 is not on the registry yet, so the verified install today is a linked checkout. Both forms are covered below: once 0.1.0 is published, add takes the bare package name and the checkout is only needed when you edit the source.
Web profile
Once 0.1.0 is on the registry this is the whole install:
dsh plugin --profile web add @unknowssq/better-experience
dsh --profile web --no-open
dsh plugin add writes both halves in one step: dependencies receives the package, and dsh.profile.bundles receives its name. Start the profile without --patch — see How a profile loads this plugin.
From a local checkout
To run the code you are editing instead of the published version:
cd <path to this checkout>
pnpm install
dsh plugin --profile web add "@unknowssq/better-experience@link:<path to this checkout>"
dsh --profile web --no-open
Desktop app
The Desktop app owns its profile: dsh --profile desktop --dump-config exits with error: profile "desktop" is managed exclusively by the Electron application, so there is no CLI start or inspect path. The supported route is the app's own plugin manager. On rc.5 the CLI can still perform the install itself:
dsh plugin --profile desktop add @unknowssq/better-experience
Then quit the app completely and reopen it. bundles is startup configuration; the in-app plugin refresh only remounts plugins that were already in the roster.
Verify the install
dsh plugin --profile web list
The sidebar should show a Better Experience entry, and the composer should show the optimize button and the draft meter. If the host loaded but the UI is missing, the package is not in bundles — see the table under Diagnosing a plugin that does not load.
Uninstall
dsh plugin --profile web remove @unknowssq/better-experience
This removes the dependency and the bundles entry. Per-profile settings written into cordis.patch.yml are left behind and can be deleted by hand.
Quick start
- Install and start the web profile with the commands above.
- Open the URL printed by DSH, then open Better Experience in the sidebar.
- Type a rough draft, for example 「把这段话改得更自然。」 and press the sparkle button. Only the rewritten prompt is written back into the composer; the button then becomes an undo icon that restores your draft in one click. Nothing is rewritten unless you press that button.
Configuration
Every field is volatile(): changing one takes effect immediately without restarting the host. Values live in the profile's cordis.patch.yml under - id: better-experience.
| Field | Type | Default | Meaning |
|---|---|---|---|
enabled |
boolean | true |
Mounts the optimize button, draft meter, and settings rows. Turning it off unmounts the button, so nothing can rewrite the draft. |
template |
string | "" |
Name of a reference template under .agents/prompts/<name>.md. Empty disables the few-shot block. |
balanceEnabled |
boolean | true |
Shows the balance and time-of-use price card at the sidebar foot, including the collapsed rail glyph. |
mode |
standard | professional |
standard |
Task-modeling depth: concise semantic cleanup versus complexity-aware, scene-adaptive structure. |
scene |
auto | eight scenes |
auto |
Only used in professional; auto lets the model classify the scene first. |
language |
auto | zh | en |
auto |
Output language of the optimized prompt and its metadata. |
Usage
Optimize a draft
Press the sparkle button beside Send. The request is a plain model call with no tools: the draft is wrapped as untrusted <draft> data, and the system prompt is composed as
CORE_RULES + MODE_POLICY[mode]
+ SCENE_POLICY[scene] (only when pinned) + CODING_IMPLEMENTATION_POLICY (coding only)
+ LANGUAGE_RULE[language] + template block (when pinned) + OUTPUT_CONTRACT
The model must answer with structured JSON (optimized_prompt, intent, is_existing_project, scene routing, changes, assumptions, missing_information). A failed parse gets one repair pass before the text is used verbatim.
Undo an optimization
After a rewrite, the composer button shows an undo icon instead of the sparkle. One click restores the exact pre-optimization draft and clears the undo state, so the button is the optimize action again. Editing the draft by hand also clears it. The plugin binds no undo or redo keystroke: Ctrl+Z inside the composer stays with the host editor, on its own undo history.
Balance card
Balances come from remote.account.getBalance through the host's DeepSeek account service — the plugin has no billing API of its own. Prices are static reference figures copied from the official pricing page (2026-09-10 update) and stored in the BILLING constant; they are never presented as live billing data. The peak windows are Beijing-time Mon–Fri 09:00–12:00 and 14:00–18:00, everything else is off-peak at half price, and weekends are off-peak for the whole day.
Project templates
Create a markdown file under .agents/prompts/:
---
title: 代码评审
scene: coding
vars: lang, focus
---
## 目标
……
title, scene, and vars are optional; an unknown scene degrades to auto. Templates appear in the 「参考模板」 selector, /tpl:代码评审 inserts the body, and insert / adopted counters accumulate in .agents/prompts/.usage.json.
Commands and routes
| Surface | Name | Notes |
|---|---|---|
| Host command | /be-optimize |
recordInput: false. Returns { kind: 'success', text } where text is a {"__be":1,"prompt":…,"meta":…} envelope; the client applies only prompt. |
| HTTP route | GET /plugins/better-experience/templates |
Template list. Registered only when webServer and fs both exist. |
| HTTP route | POST /plugins/better-experience/templates/usage |
Records insert / adopted counters. Same registration condition. |
Client-side registration, for reference:
| Slot | Id | What it renders |
|---|---|---|
conversation.input.activity |
better-experience-optimize |
Optimize button, which turns into undo after a rewrite |
conversation.input.left |
better-experience-draft-meter |
Token / cost meter and the three-dot check |
sidebar.panellist |
better-experience |
Sidebar entry that opens the panel |
sidebar.footer.action |
better-experience-balance |
Balance card |
main |
better-experience |
Settings panel content |
Host inject: commands, llm, agentDefaultModel. Client inject: slots, locale, remote, remote.commands, remote.account, configForms, shortcuts.
How a profile loads this plugin
Web and desktop boot the same host code and the same Web client bundle, but they resolve this plugin from different places. Three facts cover almost every "the plugin does not show up" case:
- Only a bundle reaches the browser. The client script roster is derived from the patch files of the packages listed in
dsh.profile.bundles; every enabled row whose package declaresdsh.client.platform === 'web'becomes an injected row (packages/test-support/client-runtime/src/assembly/bundle-roster.ts). The profile's owncordis.patch.ymlis not part of that roster, so an insert living only there loads the host half and leaves the UI empty. cordis.patch.ymloverrides, it does not install. Once the package is inbundles, an- id: better-experiencerow in the profile patch applies on top of the bundle's insert. That is where per-profile values live.--patchis a third layer and must not be combined with a bundle install. It re-inserts the same id, so the composed tree holds twobetter-experiencerows and the later, leaner one wins — saved settings look like they bounce back after a click.
Diagnosing a plugin that does not load
| Symptom | Cause |
|---|---|
ERR_MODULE_NOT_FOUND: Cannot find package '@unknowssq/better-experience' |
dependencies has no entry for it |
patch: entry "better-experience" not found |
The profile patch overrides an id that no bundle inserts |
Two better-experience rows in --dump-config; a selector bounces back after clicking |
Bundle install and --patch used together |
| Host commands work but the sidebar / settings UI is missing | Package is only in cordis.patch.yml, never in bundles |
| Desktop UI missing after editing the patch | App not restarted after the bundles change |
Architecture
client.js is one bundle: dsh loads third-party clients through window.__ModuleLoader__.load({ id, factory(require) }), where require() resolves the host module registry rather than the filesystem. Modularity therefore comes from in-file sections — Constants → Locale → Pure Logic → Hooks → UI Components → Styles → apply — with a one-way dependency rule and named CSS constants instead of separate files.
Full partition, dependency rules, and style lifecycle: docs/plugin-architecture.md, kept with the development checkout rather than inside the published package.
Design constraints worth knowing before editing:
- Tokens only. Every colour comes from host design tokens (
--dsw-alias-*,--dsw-radius-*,--dsw-shadow-*); no host selector is restyled, and no colour is hardcoded. One<style>element is created inapplyand removed by its cleanup function. - Left-aligned panel.
.be-settingsusesmargin:0withmax-width:560px, so heading, cards, and rows share one left edge. - Auto-width footer card.
.be-foot-cardiswidth:fit-contentcapped bymax-width:calc(100% - 8px); the left column isflex:0 1 auto; min-width:0so only the balance ellipsizes, while the price column never shrinks.
Development
node --check index.js
node --check client.js
There is no build step and no test runner in this package. Source edits are read once at load, so restart the host after changing index.js or client.js; configuration changes are volatile() and apply live.
Quality review cases
Use these drafts when reviewing changes to the Standard / Professional policies. Run each in both modes; for conversion checks, feed one mode's output back through the other. Judge by requested deliverable and dominant work, not topic keywords. These are review cases, not automated assertions.
| Case | Draft / conversion | Desired behavior |
|---|---|---|
| Simple request | “把这段话改得更自然。” | Both modes keep a concise prompt; Professional does not add role, workflow, or acceptance sections without a need. |
| Existing project, one fix | “修复设置页切换主题后下拉框仍是浅色的问题。” | Preserve the direct fix request; may direct repository inspection and a focused check, but not multi-phase scaffolding. |
| Explicit constraints | “给报表加导出功能,只改报表模块,不增加依赖,保留现有 CSV 格式。” | All constraints survive both directions of mode conversion and appear once. |
| Multi-task implementation | “增加批量导入、失败重试和导入记录页,并补上必要测试。” | Professional separates the three deliverables with meaningful acceptance checks; no repeated guidance per section. |
| Missing but inspectable detail | “把用户列表加分页。” | Do not ask for page size or API shape up front; direct inspection of existing conventions. |
| Critical ambiguity | “清理生产环境里不需要的数据。” | Ask a focused clarification; never invent deletion scope or authorization. |
| Mixed code and writing | “修复帮助中心 Markdown 页面的代码示例,再润色周边说明。” | Primary: coding; secondary: writing. |
| Research noun, writing outcome | “把我提供的访谈摘录整理成一篇清晰的总结,不要查外部资料。” | Primary: writing; no research secondary. |
Fixed mode behaviors
Use this draft to review both modes: “优化 dsh 插件的 README.md,专业重构结构,参考官方和社区插件示例,并将项目推送至 GitHub,使插件具备被收录和官方推荐载入的条件。”
| Mode | Expected behavior |
|---|---|
| Standard | Produce a concise rewrite that keeps the stated scope, clarifies the outcome, and adds at most one materially useful detail. Avoid a generic workflow. |
| Professional | Model the multi-part task: inspect the project, verify official criteria against primary sources, distinguish requirements from community conventions, restructure and check the README, then safely push only scoped changes. State unverified criteria and stop if the target is ambiguous, the remote has diverged, or authorization is unavailable. Never imply that documentation guarantees official inclusion. |
Check that the two outputs differ in task-modeling depth while both preserve the requested README work and GitHub push goal. Unsafe or ambiguous Git operations must stop without force-pushing, changing remotes, or discarding unrelated work.
Score each dimension 0–2 (failure / partial / good), tracking primary-scene accuracy separately from secondary precision and recall. For external eligibility tasks, separately score authoritative-source verification, distinction between official rules and convention, and whether unknown criteria are marked unverified. For publishing tasks, score target verification and safe stop behavior. Losing a hard constraint or changing the requested action is a blocking failure regardless of total score.
Permissions and data
- Draft text is sent to the model you have selected, and the rewritten text is written back into the composer through the host's draft API.
- Model calls consume your tokens. Each optimization is one tool-free model call, and nothing else in the plugin calls a model.
- Templates are read from
.agents/promptsin the session workspace and counters are written to.agents/prompts/.usage.json. - Balance is read through
remote.account.getBalance; prices are constants shipped with the plugin. - No telemetry, no analytics endpoint, no network request other than your configured model provider and the account service.
Compatibility
| Verified | dsh 0.2.1-rc.5, Windows, 2026-10-31 (web profile and Desktop app) |
| Minimum | engines.dsh >=0.2.1-rc.5 |
| Not verified | Headless / SDK / ACP profiles — the plugin's value is its UI, and the client half does not load there. Host logic is independent of the UI but has not been exercised in those profiles. |
| Breaking changes | This is a 0.x-era plugin tracking a 0.2.1-rc.x host; slot names, the command envelope, and the template route are the parts most likely to move. |
Distribution and listing status
Checked against the Harness plugin guide's pre-listing checklist (§6.8–6.9) and the market index generator (apps/plugin-market-generator/src/packages.ts, metadata.ts). This table records facts about this package; it is not a claim of official inclusion.
| Check | Status |
|---|---|
dsh.bundle.patch points at an existing YAML, and its name equals the package name |
Done — ./cordis.patch.yml, @unknowssq/better-experience |
engines.dsh declares a minimum version |
Done — >=0.2.1-rc.5 |
| A clean profile can install, start, and remove the plugin | Done for web and desktop — see Installation |
No postinstall or binary download |
Done — the package declares no scripts |
| README covers install commands, permissions, and uninstall | Done — Installation, Permissions and data |
| README shows a screenshot | Missing — no screenshot is committed |
| License is explicit | Done — MIT, LICENSE plus "license": "MIT" |
| The package can enter a generated market index | Done — no private flag; dependencies, engines, exports, and files are present |
| The release is on the npm registry | Pending — npm publish --dry-run packs 10 files cleanly; the publish itself needs registry credentials |
The repository carries the dsh-plugin topic |
Done — github.com/UnknowSSQ/dsh-plugin |
The name follows the dsh- prefix convention |
Not followed — @unknowssq/better-experience is scoped; the prefix is a convention, not a requirement |
| Dual-language README | Done — English and 简体中文 |
Market display metadata is read without evaluating the package: title and description come from locale/en.json (meta.title, meta.description) through the exports map, the icon resolves through exports["./icon"], and author, homepage, repository, and keywords are copied from package.json. All of those are present today.
Contributing
- Keep the single-file client constraint — do not split
client.jsinto separate modules. - Register through slots and services, never by restyling or querying host markup beyond the documented composer hooks.
- Run
node --check index.js; node --check client.jsbefore opening a PR, and re-run both locally in a profile. - For policy changes, re-run the review cases above and report scene accuracy separately from constraint preservation.
- New UI must use host design tokens and the existing
.be-*markup vocabulary; no new colour literals.
License
MIT — see LICENSE. Copyright (c) 2026 UnknowSSQ.
Changelog
- npm release — renamed to
@unknowssq/better-experienceunder MIT, withauthor,homepage,repository, andkeywordsfilled in so a market index can display it;privateremoved and the patch entry renamed to match. - Fixed mode policies — Standard uses a concise, clear rewrite; Professional adds task-specific execution and verification detail. External eligibility claims require primary-source checks, and requested pushes carry safe target, scope, and stop-condition rules.
- Balance card, final form — two-column auto-width card with a period chip counting down to the next peak / off-peak flip, a refresh button, and cache-hit prices for Flash and Pro. Earlier passes went through a single-row line, a popover detail panel, and a 54 px two-row cut before settling here.
- In-place undo instead of an adoption panel — the notes panel and the line-level hunk diff are gone; the optimize button itself becomes a one-click undo for the last rewrite. The plugin also stopped claiming
Ctrl+Z/Ctrl+Y, which had been stealing the composer's native undo. - Draft meter — token / cost estimate plus a zero-cost goal / constraints / acceptance check.
- Manual trigger only — the send-time trigger (
trigger: manual | send), its Enter / Send-button delegation, and the settings row are removed; optimization now happens only when you press the button. - Template assets —
.agents/promptstemplates with/tpl:insertion, few-shot reference, and usage counters. - Visibility switches —
enabledandbalanceEnabledunmount their surfaces rather than disabling them, so switching off the balance card also stops its polling. - Loaded-profile fixes — the plugin now installs as a bundle in both the web and desktop profiles; a stray apostrophe in a single-quoted string previously took the whole host module down.
No comments yet. Be the first to write one.