dsh-prompt-enhance 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -85
- package/README.zh-CN.md +236 -0
- package/lib/client.js +206 -90
- package/lib/index.js +118 -84
- package/lib/index.js.map +3 -3
- package/package.json +6 -2
- package/src/client/EnhanceButton.tsx +20 -12
- package/src/client/ResultPanel.tsx +62 -4
- package/src/client/locales.ts +12 -2
- package/src/client/shortcut.ts +4 -1
- package/src/client/styles.ts +78 -37
- package/src/client/ui-state.ts +19 -1
- package/src/config.ts +19 -7
- package/src/enhance-command.ts +5 -23
- package/src/enhance-routes.ts +26 -71
- package/src/enhancer.ts +12 -2
- package/src/http.ts +10 -6
- package/src/loopback.ts +32 -7
- package/src/orchestrate.ts +89 -0
- package/src/prompts.ts +5 -3
- package/src/shared/protocol.ts +7 -1
package/README.md
CHANGED
|
@@ -1,120 +1,237 @@
|
|
|
1
1
|
# dsh-prompt-enhance
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/rongxingda/dsh-prompt-enhance/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/dsh-prompt-enhance)
|
|
5
|
+
[](./LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
**Prompt enhancement for the [DeepSeek Harness](https://github.com/deepseek-ai) web GUI** — one click turns a rough composer draft into a well-structured prompt: explicit role and goal, executable steps, output format, acceptance criteria, and edge cases. Your intent is never changed, nothing is fabricated, and the original draft is always preserved.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
- **浏览器半区**(`exports "./client"`):输入框工具栏增强按钮(`conversation.input.right`)、增强前后对比预览面板、撤销条(`conversation.input.dock`)、可配置全局快捷键。
|
|
9
|
+
English | [简体中文](./README.zh-CN.md)
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
---
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
## Why
|
|
14
|
+
|
|
15
|
+
Good agent prompts state *who the model should be*, *what to deliver*, *in what format*, and *how success is judged*. Most drafts don't. This plugin adds a **WorkBuddy-style "enhance" affordance** to the dsh composer: it sends your draft through a low-temperature rewrite pass (via the harness's own LLM service), shows the result next to the original, and lets you apply, copy, or discard it. Undo is one click away, and every failure path leaves your draft exactly as you typed it.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
| | |
|
|
13
20
|
|---|---|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
21
|
+
| ✨ **Composer button** | A small button in the input box's tool row (next to the send button), always at hand |
|
|
22
|
+
| 🔀 **Preview panel** | Original vs. enhanced side by side, with model name and elapsed time |
|
|
23
|
+
| ↩️ **One-click undo** | After applying, a quiet bar above the composer restores the original draft |
|
|
24
|
+
| ⌨️ **Shortcut** | Default `Ctrl+Alt+E`, fully configurable, acts on the composer you are working in |
|
|
25
|
+
| 💬 **`/enhance` command** | Rewrite any text from the slash-command plane; the result never enters model history |
|
|
26
|
+
| 🧠 **Model routing** | Settings pair → current session model → harness default model, in that order |
|
|
27
|
+
| 🔑 **Zero credential setup** | Calls ride the harness LLM service; keys come from the harness credential store |
|
|
28
|
+
| ⚙️ **Live settings** | Every knob (model override, temperature, budgets, system prompt, shortcut) hot-applies from Settings → 插件配置 |
|
|
29
|
+
| 🛡️ **Draft safety** | Empty, over-length, images-only, and command-chip inputs are rejected locally; upstream failures are mapped to readable messages; the draft is never mutated on failure |
|
|
30
|
+
|
|
31
|
+
## How it works
|
|
32
|
+
|
|
33
|
+
```mermaid
|
|
34
|
+
flowchart LR
|
|
35
|
+
A[Composer draft] --> B{Local guards<br/>empty / length / chips / busy}
|
|
36
|
+
B -- pass --> C["POST /prompt-enhance/enhance<br/>(loopback-fenced host route)"]
|
|
37
|
+
B -- reject --> P[Preview panel:<br/>readable error, draft untouched]
|
|
38
|
+
C --> D["ctx.llm.stream<br/>rewrite with system strategy"]
|
|
39
|
+
D --> E[Normalize:<br/>strip fences, trim, refuse empty]
|
|
40
|
+
E --> F[Preview panel:<br/>original | enhanced]
|
|
41
|
+
F -- Apply --> G["setDraft(enhanced)<br/>original pushed to undo stack"]
|
|
42
|
+
F -- Cancel / Copy --> H[Draft untouched]
|
|
43
|
+
G --> U[Undo bar: one click restores]
|
|
44
|
+
```
|
|
17
45
|
|
|
18
|
-
|
|
46
|
+
The plugin is one npm package with two halves, following the dsh plugin conventions:
|
|
19
47
|
|
|
20
|
-
|
|
48
|
+
- **Host half** (`exports "."`, Node): registers the `prompt-enhance` settings section (schemastery — rendered automatically by the built-in plugin config page), the `POST /prompt-enhance/enhance` route on the shared webserver (loopback-fenced, body-capped), and the `/enhance` slash command. The model call goes through `ctx.llm.stream` with a normalized output pass — the same auxiliary-call discipline the harness applies to session titles: deadline + caller cancellation rechecked during and after the stream, terminal-finish validation, tool-call rejection.
|
|
49
|
+
- **Browser half** (`exports "./client"`): registers the enhance button into the `conversation.input.right` slot and the undo bar into `conversation.input.dock`, binds a live mirror of the settings namespace, and installs the global shortcut. All copy is localized (zh/en) through the harness locale system.
|
|
21
50
|
|
|
22
|
-
|
|
23
|
-
2. 当前会话已记录的请求路由(跟随会话模型);
|
|
24
|
-
3. 全局默认模型(`agent-default-model` 设置)。
|
|
51
|
+
## Requirements
|
|
25
52
|
|
|
26
|
-
|
|
53
|
+
- `dsh >= 0.1.1-rc.1`
|
|
54
|
+
- Node `^22.19.0 || >=24.0.0` (for building from source)
|
|
27
55
|
|
|
28
|
-
##
|
|
56
|
+
## Install
|
|
29
57
|
|
|
30
|
-
|
|
31
|
-
|---|---|---|
|
|
32
|
-
| `enabled` | `true` | 总开关;关闭隐藏按钮并停用所有触发 |
|
|
33
|
-
| `provider` / `model` | 空 | 成对覆盖模型路由;留空跟随当前会话 |
|
|
34
|
-
| `temperature` | `0.3` | 低温改写更忠实原意 |
|
|
35
|
-
| `maxOutputTokens` | `2048` | 单次输出上限 |
|
|
36
|
-
| `maxInputChars` | `12000` | 输入字数上限;**超限拒绝而不截断**(避免改变原意) |
|
|
37
|
-
| `timeoutMs` | `60000` | 单次超时(毫秒) |
|
|
38
|
-
| `systemPrompt` | 内置策略 | 留空用内置增强策略;可整体替换 |
|
|
39
|
-
| `shortcut` | `ctrl+alt+e` | 快捷键(如 `ctrl+shift+p`);留空禁用 |
|
|
40
|
-
|
|
41
|
-
配置热生效:每次增强调用前重读设置,改完保存即作用于下一次调用。
|
|
42
|
-
|
|
43
|
-
## 异常与边界
|
|
44
|
-
|
|
45
|
-
- **空输入 / 仅空白 / 零宽字符**:本地拦截,提示「输入框为空」。
|
|
46
|
-
- **超长输入**:本地 + 宿主双重校验,拒绝并给出字数,不截断。
|
|
47
|
-
- **仅图片无文本**:提示仅支持文本。
|
|
48
|
-
- **含命令 / 文件引用 chip**(如 `/compact`、`@file`):回填会破坏 chip,拒绝并提示先移除。
|
|
49
|
-
- **提交中 / 请求在途**:防重入,提示稍候;面板加载中可取消(请求随之中止)。
|
|
50
|
-
- **上游失败**:按稳定错误码映射为可读中文(AUTH → 鉴权失败、RATE_LIMIT → 限流、QUOTA_EXCEEDED → 配额不足、CONTEXT_WINDOW_EXCEEDED → 超上下文等),附「原输入未改动」。
|
|
51
|
-
- **模型返回空 / 包裹代码围栏 / 请求工具调用**:规范化与校验兜底(剥围栏、拒绝空结果),错误可直接重试。
|
|
52
|
-
- **浏览器中途关闭 / 切换会话**:宿主路由侦测连接断开即中止模型调用;面板与撤销栈按会话隔离,随会话清理。
|
|
53
|
-
- 所有失败只出现在插件自己的面板中,输入框草稿永不被动丢失,可继续手动输入。
|
|
54
|
-
|
|
55
|
-
## 安装
|
|
58
|
+
From npm (recommended):
|
|
56
59
|
|
|
57
60
|
```bash
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
npm run build # tsc 类型检查 + esbuild 产出 lib/index.js(ESM)与 lib/client.js(ModuleLoader 包裹)
|
|
61
|
-
dsh plugin --profile web add link:C:\path\to\dsh-prompt-enhance
|
|
62
|
-
# 重启 dsh web
|
|
61
|
+
dsh plugin --profile web add dsh-prompt-enhance
|
|
62
|
+
# restart dsh web
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
From GitHub (the built `lib/` is committed, so no local build happens):
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
68
|
dsh plugin --profile web add github:rongxingda/dsh-prompt-enhance
|
|
69
|
-
# 重启 dsh web
|
|
70
69
|
```
|
|
71
70
|
|
|
72
|
-
|
|
71
|
+
From a local checkout (for development — changes rebuild + restart take effect):
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
```bash
|
|
74
|
+
git clone https://github.com/rongxingda/dsh-prompt-enhance.git
|
|
75
|
+
cd dsh-prompt-enhance && npm install && npm run build
|
|
76
|
+
dsh plugin --profile web add link:C:\path\to\dsh-prompt-enhance
|
|
77
|
+
```
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
Uninstall:
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
```bash
|
|
82
|
+
dsh plugin --profile web remove dsh-prompt-enhance
|
|
83
|
+
# check the profile's package.json `dsh.profile.bundles` array for a leftover
|
|
84
|
+
# "dsh-prompt-enhance" row and remove it if present, then restart dsh web
|
|
85
|
+
```
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
## Usage
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
**Composer button / shortcut** — type (or leave) a draft, hit ✨ or `Ctrl+Alt+E`:
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
```
|
|
91
|
+
1. Local guards run first: empty drafts, over-length drafts (never auto-truncated — that would change your meaning), images-only drafts, and drafts containing command or file-reference chips are refused with a clear message. Chips are rejected because filling back would destroy them.
|
|
92
|
+
2. The preview panel opens with a cancellable spinner. Your draft stays untouched — the hint says so.
|
|
93
|
+
3. The result phase shows both texts side by side. **Apply** fills the enhanced text back and raises the undo bar; **Copy** puts it on the clipboard; **Cancel** (or `Esc`, or clicking the overlay) discards everything.
|
|
94
|
+
4. The undo bar sits above the composer: one click restores the original. If you keep typing after applying, the bar quietly retires itself so stale text can never overwrite newer edits.
|
|
91
95
|
|
|
92
|
-
|
|
96
|
+
**`/enhance <text>`** — rewrite any text from the slash menu. The result renders in the command plane (copyable) and never enters the conversation history or the model's context. To enhance the *composer draft itself*, use the button or shortcut — the draft lives in the browser.
|
|
97
|
+
|
|
98
|
+
**Language consistency** is guaranteed by the rewrite strategy: Chinese in, Chinese out; English in, English out.
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
Everything lives in the `prompt-enhance` settings namespace, edited from the web GUI's **Settings → 插件配置** page. Changes apply to the very next call — no restart.
|
|
103
|
+
|
|
104
|
+
| Field | Default | Description |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `enabled` | `true` | Master switch; off hides the button and disables every trigger |
|
|
107
|
+
| `provider` + `model` | empty | Explicit route override; must be filled as a **pair** (or both empty to follow the current session model) |
|
|
108
|
+
| `temperature` | `0.3` | Low temperature keeps the rewrite faithful to the original |
|
|
109
|
+
| `maxOutputTokens` | `2048` | Output token budget of one enhancement call |
|
|
110
|
+
| `maxInputChars` | `12000` | Input character cap; over-limit drafts are **rejected, never truncated** |
|
|
111
|
+
| `timeoutMs` | `60000` | End-to-end deadline of one call |
|
|
112
|
+
| `systemPrompt` | built-in strategy | Replace the whole enhancement strategy if you prefer your own |
|
|
113
|
+
| `shortcut` | `ctrl+alt+e` | Global shortcut spec (at least one modifier + one alphanumeric/function key — bare keys are ignored so normal typing can never be swallowed); empty disables it |
|
|
114
|
+
| `provider` + `model` values | — | Match the harness settings: each key under `llm-pi-ai.providers` (e.g. `zhipu`, `muyuu`) is a provider and each `models[].id` under it (e.g. `glm-5.3-flash`) is a model. Example pair: `provider: zhipu` + `model: glm-5.3-flash` |
|
|
115
|
+
|
|
116
|
+
**Model routing precedence:** explicit settings pair → the route recorded in the current session's request header → the harness-wide default model (`agent-default-model`). If none of them names a route (e.g. a fresh session with no default model), the plugin fails with an actionable message instead of guessing.
|
|
117
|
+
|
|
118
|
+
## The built-in enhancement strategy
|
|
119
|
+
|
|
120
|
+
The default system prompt instructs the model to be a prompt-rewriting expert and to apply what the draft actually needs:
|
|
121
|
+
|
|
122
|
+
1. **Role and goal** — state who the assistant acts as and what the deliverable is.
|
|
123
|
+
2. **Context and constraints** — add only what the draft implies; never invent facts, data, names, or requirements.
|
|
124
|
+
3. **Steps** — break vague or multi-part requests into numbered, executable steps.
|
|
125
|
+
4. **Output format** — specify structure, language, length, and style where implied.
|
|
126
|
+
5. **Acceptance criteria** — state how to recognize a correct result.
|
|
127
|
+
6. **Boundary conditions** — list edge cases and what to do when information is missing.
|
|
128
|
+
|
|
129
|
+
Hard rules: preserve intent exactly (never remove, alter, or contradict user information); never fabricate — insert an explicit placeholder like `(待补充:…)` / `(TBD: …)` for unknown details; keep the scope unchanged; output **only** the rewritten body (no explanations, fences, or pleasantries); mirror the input language; stay within roughly 1–3× the original length; lightly polish already-well-formed prompts instead of inflating them.
|
|
130
|
+
|
|
131
|
+
Set `systemPrompt` in the settings to replace all of this with your own strategy.
|
|
132
|
+
|
|
133
|
+
## Error handling & edge cases
|
|
134
|
+
|
|
135
|
+
| Case | Behavior |
|
|
136
|
+
|---|---|
|
|
137
|
+
| Empty / whitespace / invisible-only draft | Local refusal: "input box is empty" |
|
|
138
|
+
| Draft over `maxInputChars` | Rejected locally and at the route with exact counts; **no auto-truncation** |
|
|
139
|
+
| Images attached but no text | Refused: text-only feature |
|
|
140
|
+
| Command or file-reference chips in the draft | Refused: fill-back would destroy the chips |
|
|
141
|
+
| Submitting / busy phase or a request already in flight | Refused with "try again in a moment" |
|
|
142
|
+
| Upstream model failure | Stable codes mapped to readable hints: `AUTH` → check API key, `RATE_LIMIT` → retry later, `QUOTA_EXCEEDED` → check balance, `CONTEXT_WINDOW_EXCEEDED` → shorten input, `NO_ADAPTER`/unconfigured → configure a model |
|
|
143
|
+
| Output reaches `maxOutputTokens` | Refused with a hint to raise the cap or shorten the draft |
|
|
144
|
+
| Model returns empty / fence-wrapped / tool-call output | Normalized (fences stripped) or refused; retryable |
|
|
145
|
+
| Timeout | `504`-mapped message with the configured seconds; retryable |
|
|
146
|
+
| Browser tab closed mid-flight | The host route detects the disconnect and aborts the model call |
|
|
147
|
+
| Session switch | Panel state, undo stack, and shortcut targeting are per-session; switching closes the panel and clears its undo entries |
|
|
148
|
+
|
|
149
|
+
Every failure surfaces inside the plugin's own panel; the composer draft is never modified by a failed call, so manual input continues undisturbed.
|
|
150
|
+
|
|
151
|
+
## Security model
|
|
152
|
+
|
|
153
|
+
The enhance route is served by your own dsh host and reachable **only from this machine**:
|
|
154
|
+
|
|
155
|
+
- **Socket fence** — requests from non-loopback addresses are refused (`127.0.0.1` / `::1` only). Note this means *any local process* can call the route; it carries no user authentication.
|
|
156
|
+
- **Host allowlist** — the route also validates the `Host` header against `localhost` / `127.0.0.1` / `[::1]`, which defeats DNS-rebinding (a rebound attacker domain keeps the loopback socket address but carries the attacker's hostname and is refused). Responses are `cache-control: no-store`.
|
|
157
|
+
- **Not for reverse-proxy exposure** — if you put dsh web behind a proxy that listens on the LAN, external callers appear as loopback to the route and the fence is moot. Do not expose a proxied host without adding your own authentication at the proxy.
|
|
158
|
+
- **Prompt-injection boundary** — the draft is framed between `<raw_prompt>` tags, literal closing tags inside the draft are neutralized, and the strategy prompt treats the framed text as pure data. Enhancements run with your own credentials and the result is only ever shown back to you.
|
|
159
|
+
|
|
160
|
+
## Architecture
|
|
93
161
|
|
|
94
162
|
```
|
|
95
163
|
src/
|
|
96
|
-
├── index.ts
|
|
97
|
-
├── config.ts schemastery
|
|
98
|
-
├── prompts.ts
|
|
99
|
-
├── enhancer.ts ctx.llm
|
|
100
|
-
|
|
101
|
-
├── enhance-
|
|
102
|
-
├──
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
├──
|
|
109
|
-
|
|
164
|
+
├── index.ts host apply(): settings section + route + command
|
|
165
|
+
├── config.ts schemastery schema + resolution (paired route validation)
|
|
166
|
+
├── prompts.ts built-in strategy system prompt + <raw_prompt> framing
|
|
167
|
+
├── enhancer.ts the ctx.llm auxiliary call (route resolution, deadline
|
|
168
|
+
│ racing, finish validation, code → message mapping)
|
|
169
|
+
├── enhance-routes.ts POST /prompt-enhance/enhance (loopback fence, body cap)
|
|
170
|
+
├── enhance-command.ts /enhance slash command (host command registry)
|
|
171
|
+
├── loopback.ts 127.0.0.1/::1 fence for the route
|
|
172
|
+
├── http.ts bounded JSON body reader / writer
|
|
173
|
+
├── shared/ wire protocol types, input checks, output normalization
|
|
174
|
+
│ (imported by both halves)
|
|
175
|
+
└── client/ browser half
|
|
176
|
+
├── index.tsx slots registration + settings mirror + shortcut listener
|
|
177
|
+
├── EnhanceButton conversation.input.right entry: guards + call orchestration
|
|
178
|
+
├── ResultPanel overlay panel: compare / apply / copy / cancel / retry
|
|
179
|
+
├── UndoBar conversation.input.dock entry: restore affordance
|
|
180
|
+
├── ui-state.ts external store shared by components (panel, undo, sessions)
|
|
181
|
+
├── enhance-client fetch client with abort + typed errors
|
|
182
|
+
├── undo-stack.ts per-session LIFO (depth 3)
|
|
183
|
+
├── shortcut.ts pure combo parsing / matching
|
|
184
|
+
├── settings.ts client mirror of the settings namespace
|
|
185
|
+
├── locales.ts zh + en dictionaries (harness locale namespace)
|
|
186
|
+
└── styles.ts self-injected stylesheet (dsh-pe- prefixed classes)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Build outputs: `lib/index.js` (host half, ESM, package imports kept external) and `lib/client.js` (browser half bundled to CJS inside the `window.__ModuleLoader__.load({ id, factory })` envelope the dsh web shell expects). Both are committed so GitHub installs need no build step; CI fails if `lib/` drifts from `src/`.
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm install
|
|
195
|
+
npm run typecheck # tsc --noEmit
|
|
196
|
+
npm test # unit + real-http + component suites
|
|
197
|
+
npm run build # typecheck + both halves
|
|
198
|
+
npm run watch # esbuild watch for both halves
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Test coverage:** input validation, output normalization, the enhancer against stubbed `ctx.llm` streams, a real-`node:http` route suite (loopback Host fence, disconnect abort, error envelopes), the loopback/Host fence units, prompt framing, config resolution, the undo stack, shortcut parsing, client settings, and React component tests locking the enhance → apply → undo flow (guards, stale marking, diverged-draft undo semantics).
|
|
202
|
+
|
|
203
|
+
**Release process (maintainers):**
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
npm version patch # or minor / major — bumps package.json and tags
|
|
207
|
+
npm run build # make lib/ match src/
|
|
208
|
+
git push --follow-tags
|
|
209
|
+
npm publish # with 2FA OTP, or a granular token with "bypass 2FA" checked
|
|
110
210
|
```
|
|
111
211
|
|
|
112
|
-
|
|
212
|
+
CI runs typecheck + tests + build on every push/PR and rejects merges where `lib/` differs from the committed build.
|
|
213
|
+
|
|
214
|
+
## FAQ
|
|
215
|
+
|
|
216
|
+
**Why is my draft rejected when it contains `/commands` or `@references`?**
|
|
217
|
+
The fill-back writes plain text via `inputActions.setDraft`, which would destroy the chips. Remove them, enhance, then re-insert.
|
|
218
|
+
|
|
219
|
+
**Why isn't over-length input auto-truncated?**
|
|
220
|
+
Truncation silently changes your meaning — the plugin refuses and shows the exact counts instead.
|
|
221
|
+
|
|
222
|
+
**Can I pin a specific model?**
|
|
223
|
+
Fill `provider` and `model` **as a pair** in the settings (e.g. your harness-configured provider route). Leave both empty to follow the current session's model.
|
|
224
|
+
|
|
225
|
+
**Where does my draft go?**
|
|
226
|
+
Browser → your own dsh host over a loopback-only route → the harness LLM service → the configured model provider. Nothing is sent anywhere else, and the enhancement never enters the session's model history.
|
|
227
|
+
|
|
228
|
+
**Does it work with the official DeepSeek route?**
|
|
229
|
+
Yes — it rides `ctx.llm`, so any provider the harness serves (DeepSeek official, OpenAI-compatible gateways) works.
|
|
230
|
+
|
|
231
|
+
## Acknowledgments
|
|
232
|
+
|
|
233
|
+
Plugin structure, the loopback fence, and the settings-section pattern follow the conventions established by the dsh plugin family — in particular [`@linxin666/dsh-tool-describe-image`](https://www.npmjs.com/package/@linxin666/dsh-tool-describe-image).
|
|
234
|
+
|
|
235
|
+
## License
|
|
113
236
|
|
|
114
|
-
-
|
|
115
|
-
- [ ] 英文输入返回英文(语言一致性)
|
|
116
|
-
- [ ] 空输入 / 超长输入 / 含 `/命令` chip / 提交中,均被拦截且草稿不变
|
|
117
|
-
- [ ] 面板加载中点取消:草稿不变,可继续输入
|
|
118
|
-
- [ ] 拔掉 API Key 或断网:错误消息可读,重试可用
|
|
119
|
-
- [ ] 设置改 `shortcut` / `maxInputChars` / `enabled` 后无需重启即生效
|
|
120
|
-
- [ ] `/enhance 帮我写周报` 结果在命令面板展示且未发送给会话模型
|
|
237
|
+
[Apache-2.0](./LICENSE)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# dsh-prompt-enhance
|
|
2
|
+
|
|
3
|
+
[](https://github.com/rongxingda/dsh-prompt-enhance/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/dsh-prompt-enhance)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
7
|
+
**DeepSeek Harness Web GUI 的「提示词增强」插件** —— 一键把输入框里的草稿改写为结构化提示词:明确的角色与目标、可执行步骤、输出格式、验收标准、边界条件。不改变原意、不凭空编造需求,原文始终保留。
|
|
8
|
+
|
|
9
|
+
[English](./README.md) | 简体中文
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 为什么需要它
|
|
14
|
+
|
|
15
|
+
好的智能体提示词会讲清*让模型扮演谁*、*交付什么*、*以什么格式*、*怎样算合格*——而大多数草稿不会。本插件给 dsh 输入框加上 WorkBuddy 风格的「增强」入口:草稿经低温重写(走 harness 自带的 LLM 服务),结果与原文并排对比,你可以回填、复制或丢弃。撤销只需一键,且**任何失败路径都不会改动你输入的内容**。
|
|
16
|
+
|
|
17
|
+
## 功能一览
|
|
18
|
+
|
|
19
|
+
| | |
|
|
20
|
+
|---|---|
|
|
21
|
+
| ✨ **输入框按钮** | 工具栏内发送键旁的小按钮,随手可及 |
|
|
22
|
+
| 🔀 **对比预览面板** | 原文与增强结果左右并排,附模型名与耗时 |
|
|
23
|
+
| ↩️ **一键撤销** | 回填后输入框上方出现撤销条,一键恢复原文 |
|
|
24
|
+
| ⌨️ **快捷键** | 默认 `Ctrl+Alt+E` 可配置,作用于你正在使用的输入框 |
|
|
25
|
+
| 💬 **`/enhance` 命令** | 斜杠面板直接改写任意文本;结果不进入模型历史 |
|
|
26
|
+
| 🧠 **模型路由** | 设置成对覆盖 → 当前会话模型 → 全局默认模型,依次回退 |
|
|
27
|
+
| 🔑 **零凭据配置** | 调用走 harness LLM 服务,密钥来自 harness 凭据存储 |
|
|
28
|
+
| ⚙️ **配置热生效** | 模型覆盖、温度、预算、系统提示词、快捷键,全部在 设置 → 插件配置 即改即用 |
|
|
29
|
+
| 🛡️ **草稿安全** | 空输入、超长、仅图片、含命令块在本地拦截;上游失败映射为可读提示;失败绝不改动草稿 |
|
|
30
|
+
|
|
31
|
+
## 工作原理
|
|
32
|
+
|
|
33
|
+
```mermaid
|
|
34
|
+
flowchart LR
|
|
35
|
+
A[输入框草稿] --> B{本地守卫<br/>空 / 超长 / 命令块 / 占用}
|
|
36
|
+
B -- 通过 --> C["POST /prompt-enhance/enhance<br/>(仅回环的宿主路由)"]
|
|
37
|
+
B -- 拒绝 --> P[预览面板:<br/>可读错误,草稿不动]
|
|
38
|
+
C --> D["ctx.llm.stream<br/>按内置策略重写"]
|
|
39
|
+
D --> E[规范化:<br/>剥围栏 / 去空白 / 拒空]
|
|
40
|
+
E --> F[预览面板:<br/>原文 | 增强结果]
|
|
41
|
+
F -- 回填 --> G["setDraft(增强文本)<br/>原文压入撤销栈"]
|
|
42
|
+
F -- 取消 / 复制 --> H[草稿不动]
|
|
43
|
+
G --> U[撤销条:一键恢复]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
插件是单个 npm 包、双半区结构,完全遵循 dsh 插件规范:
|
|
47
|
+
|
|
48
|
+
- **宿主半区**(`exports "."`,Node):注册 `prompt-enhance` 设置节(schemastery,由内置插件配置页自动渲染)、共享 webserver 上的 `POST /prompt-enhance/enhance` 路由(仅回环、限长),以及 `/enhance` 斜杠命令。模型调用走 `ctx.llm.stream` 并做规范化处理——与 harness 对会话标题相同的辅助调用纪律:超时与调用方取消在流过程中和结束后复查、终结 finish 校验、拒绝工具调用。
|
|
49
|
+
- **浏览器半区**(`exports "./client"`):把增强按钮注册进 `conversation.input.right` 插槽、撤销条注册进 `conversation.input.dock` 插槽,绑定设置命名空间的实时镜像,并安装全局快捷键。全部文案经 harness locale 系统提供中英双语。
|
|
50
|
+
|
|
51
|
+
## 环境要求
|
|
52
|
+
|
|
53
|
+
- `dsh >= 0.1.1-rc.1`
|
|
54
|
+
- Node `^22.19.0 || >=24.0.0`(仅从源码构建时需要)
|
|
55
|
+
|
|
56
|
+
## 安装
|
|
57
|
+
|
|
58
|
+
从 npm(推荐):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
dsh plugin --profile web add dsh-prompt-enhance
|
|
62
|
+
# 重启 dsh web
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
从 GitHub(构建产物 `lib/` 已随仓库提交,安装无需本地构建):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
dsh plugin --profile web add github:rongxingda/dsh-prompt-enhance
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
从本地检出(开发用,改代码重建 + 重启即生效):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
git clone https://github.com/rongxingda/dsh-prompt-enhance.git
|
|
75
|
+
cd dsh-prompt-enhance && npm install && npm run build
|
|
76
|
+
dsh plugin --profile web add link:C:\path\to\dsh-prompt-enhance
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
卸载:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
dsh plugin --profile web remove dsh-prompt-enhance
|
|
83
|
+
# 检查 profile 的 package.json 中 `dsh.profile.bundles` 数组是否残留
|
|
84
|
+
# "dsh-prompt-enhance" 行,有则手动删除,然后重启 dsh web
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 使用方法
|
|
88
|
+
|
|
89
|
+
**输入框按钮 / 快捷键** —— 输入(或留着)草稿,点 ✨ 或按 `Ctrl+Alt+E`:
|
|
90
|
+
|
|
91
|
+
1. 先跑本地守卫:空草稿、超长草稿(**不自动截断**——截断会改变原意)、仅图片草稿、含命令或文件引用块的草稿,都会被拒绝并给出明确提示。引用块被拒是因为回填会破坏它们。
|
|
92
|
+
2. 预览面板打开,显示可取消的加载动画。此时草稿原封不动——面板上写明了这一点。
|
|
93
|
+
3. 结果阶段左右并排展示两份文本。**回填**把增强文本写回输入框并升起撤销条;**复制**进剪贴板;**取消**(`Esc` 或点击遮罩)丢弃一切。
|
|
94
|
+
4. 撤销条停在输入框上方:一键恢复原文。回填后你继续输入,撤销条会安静地自我退位——过期的原文永远不会覆盖你更新的编辑。
|
|
95
|
+
|
|
96
|
+
**`/enhance <文本>`** —— 从斜杠菜单改写任意文本。结果在命令面板渲染(可复制),不进入会话历史与模型上下文。要增强**输入框里的草稿**请用按钮或快捷键——草稿在浏览器里。
|
|
97
|
+
|
|
98
|
+
**语言一致性**由重写策略保证:中文进中文出,英文进英文出。
|
|
99
|
+
|
|
100
|
+
## 配置
|
|
101
|
+
|
|
102
|
+
全部配置位于 `prompt-enhance` 设置命名空间,在 Web GUI 的 **设置 → 插件配置** 页编辑。改动作用于下一次调用——无需重启。
|
|
103
|
+
|
|
104
|
+
| 字段 | 默认 | 说明 |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `enabled` | `true` | 总开关;关闭隐藏按钮并停用所有触发方式 |
|
|
107
|
+
| `provider` + `model` | 空 | 显式路由覆盖;必须**成对**填写(或都留空以跟随当前会话模型) |
|
|
108
|
+
| `temperature` | `0.3` | 低温使改写更忠实于原意 |
|
|
109
|
+
| `maxOutputTokens` | `2048` | 单次增强调用的输出 token 预算 |
|
|
110
|
+
| `maxInputChars` | `12000` | 输入字数上限;超限**拒绝而不截断** |
|
|
111
|
+
| `timeoutMs` | `60000` | 单次调用的端到端超时 |
|
|
112
|
+
| `systemPrompt` | 内置策略 | 想用自己的增强策略时整体替换 |
|
|
113
|
+
| `shortcut` | `ctrl+alt+e` | 快捷键规格(至少一个修饰键 + 单个字母/数字/功能键——纯裸键会被忽略,绝不会吞掉正常打字);留空禁用 |
|
|
114
|
+
| `provider` + `model` 取值 | — | 与 harness 设置一致:设置文件 `llm-pi-ai.providers` 下的键就是 provider(如 `zhipu`、`muyuu`),其 `models[].id` 就是 model(如 `glm-5.3-flash`)。示例:`provider: zhipu` + `model: glm-5.3-flash` |
|
|
115
|
+
|
|
116
|
+
**模型路由优先级:** 设置成对覆盖 → 当前会话请求头中记录的路由 → 全局默认模型(`agent-default-model`)。三者都指不出路由时(例如全新会话且无默认模型),插件给出可操作的报错而不是乱猜。
|
|
117
|
+
|
|
118
|
+
## 内置增强策略
|
|
119
|
+
|
|
120
|
+
默认系统提示词让模型扮演提示词重写专家,按草稿的实际需要施加:
|
|
121
|
+
|
|
122
|
+
1. **角色与目标** —— 写明助手扮演谁、交付物是什么。
|
|
123
|
+
2. **背景与约束** —— 只补全草稿可推断的信息;绝不编造事实、数据、名称或需求。
|
|
124
|
+
3. **步骤** —— 把模糊或多头需求拆成编号、可执行的步骤。
|
|
125
|
+
4. **输出格式** —— 在有暗示处指明结构、语言、长度与风格。
|
|
126
|
+
5. **验收标准** —— 写明怎样判断结果正确。
|
|
127
|
+
6. **边界条件** —— 列出边界情况与信息缺失时的做法。
|
|
128
|
+
|
|
129
|
+
硬性规则:精确保留原意(不删除、不篡改、不与用户信息冲突);绝不编造——未知细节插入显式占位符如 `(待补充:…)` / `(TBD: …)`;保持任务范围不变;**只输出**改写后的正文(无解释、无围栏、无客套);镜像输入语言;长度约为原文 1–3 倍;已经成形的提示词只做轻度润色而不注水。
|
|
130
|
+
|
|
131
|
+
在设置中填写 `systemPrompt` 即可整体替换为你的策略。
|
|
132
|
+
|
|
133
|
+
## 异常与边界
|
|
134
|
+
|
|
135
|
+
| 情形 | 行为 |
|
|
136
|
+
|---|---|
|
|
137
|
+
| 空 / 纯空白 / 零宽字符草稿 | 本地拒绝:「输入框为空」 |
|
|
138
|
+
| 草稿超过 `maxInputChars` | 本地与路由双重拒绝并给出精确字数;**不自动截断** |
|
|
139
|
+
| 只附加了图片没有文本 | 拒绝:仅支持文本 |
|
|
140
|
+
| 草稿含命令 / 文件引用块 | 拒绝:回填会破坏引用块 |
|
|
141
|
+
| 提交中 / 占用阶段 / 已有请求在途 | 拒绝并提示「稍后再试」 |
|
|
142
|
+
| 上游模型失败 | 稳定错误码映射为可读提示:`AUTH` → 检查 API Key、`RATE_LIMIT` → 稍后重试、`QUOTA_EXCEEDED` → 检查余额、`CONTEXT_WINDOW_EXCEEDED` → 精简输入、`NO_ADAPTER`/未配置 → 先配置模型 |
|
|
143
|
+
| 输出达到 `maxOutputTokens` | 拒绝并提示调大上限或精简原文 |
|
|
144
|
+
| 模型返回空 / 围栏包裹 / 工具调用 | 规范化(剥围栏)或拒绝;可重试 |
|
|
145
|
+
| 超时 | 映射为 `504` 的提示并给出配置秒数;可重试 |
|
|
146
|
+
| 浏览器中途关闭 | 宿主路由侦测连接断开,立即中止模型调用 |
|
|
147
|
+
| 切换会话 | 面板状态、撤销栈、快捷键目标均按会话隔离;切换即关闭面板并清理撤销记录 |
|
|
148
|
+
|
|
149
|
+
所有失败只出现在插件自己的面板里;失败的调用绝不修改输入框草稿,手动输入不受任何干扰。
|
|
150
|
+
|
|
151
|
+
## 安全模型
|
|
152
|
+
|
|
153
|
+
增强路由由你自己的 dsh 宿主提供服务,**仅限本机访问**:
|
|
154
|
+
|
|
155
|
+
- **套接字栅栏** —— 非回环地址(`127.0.0.1` / `::1` 之外)的请求一律拒绝。注意这意味着*本机任意进程*都可以调用该路由;路由本身不带用户鉴权。
|
|
156
|
+
- **Host 白名单** —— 路由同时校验 `Host` 头是否为 `localhost` / `127.0.0.1` / `[::1]`,可防御 DNS 重绑定(重绑定的攻击者域名虽然落在回环套接字上,但携带的是攻击者主机名,会被拒绝)。响应均为 `cache-control: no-store`。
|
|
157
|
+
- **不要经反向代理暴露** —— 若把 dsh web 放在监听局域网的代理后面,外部调用者在路由看来就是回环地址,栅栏形同虚设。除非在代理层自行加鉴权,否则不要暴露。
|
|
158
|
+
- **提示注入边界** —— 草稿被框在 `<raw_prompt>` 标签之间,草稿内的字面闭合标签会被中和,策略提示词把框内文本视为纯数据。增强只使用你自己的凭据,结果也只回显给你本人。
|
|
159
|
+
|
|
160
|
+
## 架构
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
src/
|
|
164
|
+
├── index.ts 宿主 apply():设置节 + 路由 + 命令
|
|
165
|
+
├── config.ts schemastery 模式 + 解析校验(成对路由)
|
|
166
|
+
├── prompts.ts 内置策略系统提示词 + <raw_prompt> 框架
|
|
167
|
+
├── enhancer.ts ctx.llm 辅助调用(路由解析、超时竞速、finish 校验、
|
|
168
|
+
│ 错误码 → 文案映射)
|
|
169
|
+
├── enhance-routes.ts POST /prompt-enhance/enhance(回环栅栏、限长)
|
|
170
|
+
├── enhance-command.ts /enhance 斜杠命令(宿主命令注册表)
|
|
171
|
+
├── loopback.ts 路由的 127.0.0.1/::1 栅栏
|
|
172
|
+
├── http.ts 限长 JSON body 读取 / 写出
|
|
173
|
+
├── shared/ 传输协议类型、输入校验、输出规范化(两端共用)
|
|
174
|
+
└── client/ 浏览器半区
|
|
175
|
+
├── index.tsx 插槽注册 + 设置镜像 + 快捷键监听
|
|
176
|
+
├── EnhanceButton conversation.input.right 条目:守卫链 + 调用编排
|
|
177
|
+
├── ResultPanel 浮层面板:对比 / 回填 / 复制 / 取消 / 重试
|
|
178
|
+
├── UndoBar conversation.input.dock 条目:恢复入口
|
|
179
|
+
├── ui-state.ts 组件共享的外部 store(面板、撤销、会话注册表)
|
|
180
|
+
├── enhance-client fetch 客户端(可中止 + 类型化错误)
|
|
181
|
+
├── undo-stack.ts 按会话的 LIFO 栈(深度 3)
|
|
182
|
+
├── shortcut.ts 纯函数的组合键解析 / 匹配
|
|
183
|
+
├── settings.ts 设置命名空间的客户端镜像
|
|
184
|
+
├── locales.ts 中英文字典(harness locale 命名空间)
|
|
185
|
+
└── styles.ts 自注入样式表(dsh-pe- 前缀类名)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
构建产物:`lib/index.js`(宿主半区,ESM,包引用保持外部)与 `lib/client.js`(浏览器半区,打包为 CJS 并包进 dsh web 壳要求的 `window.__ModuleLoader__.load({ id, factory })` 信封)。两者都随仓库提交,因此 GitHub 直装无需构建;CI 会在 `lib/` 与 `src/` 不一致时拒绝合并。
|
|
189
|
+
|
|
190
|
+
## 开发
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm install
|
|
194
|
+
npm run typecheck # tsc --noEmit
|
|
195
|
+
npm test # 单元 + 真实 HTTP + 组件测试套件
|
|
196
|
+
npm run build # 类型检查 + 双半区构建
|
|
197
|
+
npm run watch # esbuild 监听构建双半区
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**测试覆盖:** 输入校验(空 / 零宽字符 / 超长)、输出规范化(剥围栏、折行、拒空)、以桩 `ctx.llm` 流驱动的增强器(正常路径、AUTH/NO_ADAPTER/max-tokens 终结、空输出、挂起流失超时、预中止的调用方、路由优先级)、路由的真实 `node:http` 端到端套件(回环栅栏、方法守卫、畸形 body、结构化错误信封、停用开关)、撤销栈、快捷键解析与匹配、配置解析、客户端设置解码器。
|
|
201
|
+
|
|
202
|
+
**发版流程(维护者):**
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm version patch # 或 minor / major —— 更新 package.json 并打 tag
|
|
206
|
+
npm run build # 让 lib/ 与 src/ 一致
|
|
207
|
+
git push --follow-tags
|
|
208
|
+
npm publish # 带 2FA OTP,或使用勾选了 "bypass 2FA" 的细粒度令牌
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
CI 在每次 push/PR 上运行类型检查 + 测试 + 构建,并在 `lib/` 与已提交构建不一致时拒绝合并。
|
|
212
|
+
|
|
213
|
+
## FAQ
|
|
214
|
+
|
|
215
|
+
**为什么草稿里有 `/命令` 或 `@引用` 时被拒绝?**
|
|
216
|
+
回填通过 `inputActions.setDraft` 写纯文本,会破坏引用块。先移除它们,增强后再插回。
|
|
217
|
+
|
|
218
|
+
**为什么不自动截断超长输入?**
|
|
219
|
+
截断会无声地改变你的原意——插件宁可拒绝并给出精确字数。
|
|
220
|
+
|
|
221
|
+
**能固定用某个模型吗?**
|
|
222
|
+
在设置中**成对**填写 `provider` 与 `model`(例如 harness 已配置的 provider 路由)。都留空则跟随当前会话模型。
|
|
223
|
+
|
|
224
|
+
**我的草稿会被发到哪里?**
|
|
225
|
+
浏览器 → 你自己的 dsh 宿主(仅回环路由)→ harness LLM 服务 → 配置的模型服务商。不会发往其他任何地方,增强行为也绝不进入会话的模型历史。
|
|
226
|
+
|
|
227
|
+
**官方 DeepSeek 路由能用吗?**
|
|
228
|
+
能——调用走 `ctx.llm`,harness 支持的所有 provider(DeepSeek 官方、OpenAI 兼容网关)都可用。
|
|
229
|
+
|
|
230
|
+
## 致谢
|
|
231
|
+
|
|
232
|
+
插件结构、回环栅栏与设置节模式遵循 dsh 插件家族的既有惯例——特别是 [`@linxin666/dsh-tool-describe-image`](https://www.npmjs.com/package/@linxin666/dsh-tool-describe-image)。
|
|
233
|
+
|
|
234
|
+
## 许可证
|
|
235
|
+
|
|
236
|
+
[Apache-2.0](./LICENSE)
|