Skip to content
dsh-market Browse plugins GitHub 中文

seven282/oss-prompt-optimizer

Optimize a raw instruction into a professional prompt through the harness llm service — four-section (Role / Task / Context / Format) or a headerless plain-text style. Context-aware by default: the recent conversation is injected as background and may enrich the ## Context section. Long outputs resume from the truncation point with auto-expanded budgets; validated results are cached (identical requests cost zero model calls); a unified call budget bounds worst-case cost. Composer ✨ one-click optimize, undo, cancel and a token-cost hint; prompt_optimize tool (optimize + iterate), auto-optimize hook, auto-detected zh/en role document.

Stars ★ 4 Category Tools & Capabilities Listed 2026-08-16 npm oss-prompt-optimizer

Install

Inside DeepSeek Harness, with dsh-market

dsh plugin --profile web add dshmarket

Or from the command line

dsh plugin --profile web add oss-prompt-optimizer

Installing runs third-party code with your own permissions — it can read your files, use your credentials and reach the network. Review the source first, and pin a commit (github:owner/repo#sha) when you can.

README

简体中文 | English

prompt-optimizer is a DeepSeek Harness plugin that rewrites raw, unstructured instructions into professional, ready-to-use prompts — the same experience as Qoder and Codex.

By default the result is a heading-free plain-text prompt (outputStyle: 'plain', fewer tokens); a four-section structured style (outputStyle: 'sections'## Role / ## Task / ## Context / ## Format) is configurable. The optimization is driven by a built-in meta-prompt and run through the harness LLM service — the plugin never calls any external API and never touches credentials.

Features

  • Output styles — four-section prompts by default, or a heading-free plain-text style (outputStyle: 'plain') that saves tokens.
  • Tool — agents can call the prompt_optimize tool with an instruction and receive the optimized prompt back; passing a previous result as lastOptimized together with iterateInstruction iterates on it instead.
  • Service — other plugins can call ctx.promptOptimizer.optimize(rawInput, { signal }) or ctx.promptOptimizer.iterate(lastOptimized, instruction, { signal }); the browser side can call them via ctx.remote.promptOptimizer.
  • Input box ✨ button — a persistent icon in the composer toolbar: click to optimize the current draft and write the result back, with one-click undo; clicking again while optimizing cancels (AbortSignal), and a transient "≈N tokens" cost hint appears after a fresh optimization.
  • Role-document language auto-detection — the optimizer's role document (its meta-prompt) follows the instruction's language by default: CJK-dominant input uses the Chinese role document, anything else the English one (see below).
  • Auto-optimize hook (optional, off by default) — user messages starting with a trigger prefix (e.g. /optimize ) are optimized before they reach the model.
  • Context awareness (on by default) — the recent conversation before the instruction is injected into the meta-prompt ("pure data / background reference" guardrail) so the result fits prior discussion; set contextAware: false to disable (see the config table below).
  • Post-validation with retry — when the output misses sections / is too thin / too short, the pipeline retries (configurable count), injecting a diagnosis of the previous failure (missing section names, thin sections with character counts) into the next call's system prompt; if it still fails, the original instruction / previous result is returned with an explanation and a stable machine-readable error code (OptimizeResult.errorCode: MISSING_SECTIONS / THIN_SECTIONS / THIN_OUTPUT / TIMEOUT / NO_MODEL_ROUTE …), rendered as a [error-code] prefix in tool failures.
  • Safety rails — output always carries the four sections; empty input errors out; oversized input is truncated; cancellation signals are forwarded.

Screenshot Screenshot

Installation

Published on npm (oss-prompt-optimizer). Pick any of the three ways:

Option 1: npm (recommended, no build permission needed)

dsh plugin --profile web add oss-prompt-optimizer

Option 2: from GitHub (source build, requires prepare permission)

dsh plugin --profile web add github:seven282/oss-prompt-optimizer
# pnpm ≥10 refuses to run prepare on first install; add the package key pnpm
# suggests to that profile's pnpm-workspace.yaml and retry:
#   allowBuilds:
#     oss-prompt-optimizer: true
# Pin a commit: github:seven282/oss-prompt-optimizer#<sha>

Option 3: from a local directory (development)

dsh plugin --profile web add <project-path>
# Windows paths containing spaces get split; use a junction first:
#   New-Item -ItemType Junction -Path "C:\dsh-po" -Target "E:\<your-project-path>"
#   dsh plugin --profile web add C:\dsh-po

Uninstall (reversible)

dsh plugin --profile web remove oss-prompt-optimizer

Restart the harness (dsh web) after installing or removing the plugin.

Input-box ✨ button

The plugin ships a browser client (lib/client.js, loaded by the harness via the dsh.client declaration): it registers a ✨ button on the left of the composer tool row — disabled (⏳) while the input is empty or an optimization is in flight. Clicking it calls the host's promptOptimizer Remote service, optimizes the current draft, and writes the four-section prompt back into the input box (inputActions.setDraft).

One-click undo — after a successful optimization the button becomes an undo state (↺, brand color): as long as the draft is still the freshly generated result (not manually edited), clicking restores the original text. Editing the draft clears the undo state automatically (so later edits are never overwritten).

Accessibility — success / failure / undo are announced through a hidden aria-live region (screen readers).

  • No configuration needed; enabled with the plugin, effective after a harness restart.
  • It drives the same ctx.promptOptimizer.optimize() as the tool and hook, sharing all configuration (temperature, maxTokens, outputLanguage, …).

Role-document language (auto-detection)

The language of the optimizer's role document (the meta-prompt / system prompt itself) is resolved automatically from the instruction by default: input whose non-whitespace characters are ≥30% CJK ideographs (e.g. 「帮我写一份周报」) uses the Chinese role document; everything else (English, Japanese, …) uses the English one — the safe default of the two shipped versions. outputLanguage independently controls the language of the optimized result; the two do not affect each other.

Pin or restore the mode at runtime through input-box commands (session-scoped; falls back to the config after a restart):

  • /optimizer-language auto — restore auto-detection (default)
  • /optimizer-language 中文 / /optimizer-language 英文 — pin the language
  • /optimizer-language status — query the current mode

The metaPromptLanguage: 'auto' | '中文' | '英文' config (default 'auto') decides the initial mode after a restart; explicit values ('中文'/'英文') pin the language, 'auto' follows the input. No language button is shipped.

Auto-optimize toggle (commands)

The runtime "optimize every message before the model step" switch is controlled through input-box commands:

  • /auto-optimize on / /auto-optimize off / /auto-optimize toggle / /auto-optimize status

Once enabled, the host enters "optimize before sending" mode: the agent/pre-step hook optimizes every user text message (the runtime equivalent of autoOptimizeAll: true).

Auto-optimize hook

Enable it in cordis.patch.yml:

- insert:
    - id: prompt-optimizer
      name: 'prompt-optimizer'
      config:
        autoOptimize: true
        autoOptimizePrefix: '/optimize '

When enabled, any user message starting with autoOptimizePrefix is optimized by the agent/pre-step hook before it reaches the model step — the prefix is stripped, the remainder is sent as the raw instruction, and the model actually receives the optimized four-section prompt (with a short "auto-optimized" note).

  • Safety by design: off by default; per-message opt-in (only prefixed messages are optimized) — normal conversation is never touched.
  • Graceful degradation: on a non-matching prefix, an empty remainder, or an optimization failure, the original message reaches the model unchanged.
  • At most one message is optimized per step, avoiding multiple model calls within a single step.
  • The hook is registered in effect scope and removed automatically on plugin dispose.

Configuration & Commands

Set plugin options in cordis.patch.yml (every value below also has a schema default):

Key Type Default Description
temperature number 0–2 0.2 Sampling temperature
maxTokens int ≥1 1200 Max output tokens per call; lower to 600-800 to save tokens
maxRetries int 0–5 1 Extra retries when a section is missing
maxCalls int 1–20 4 Unified model-call budget per optimization (first call + expansions + retries); exceeding it degrades to the original instruction with TOO_MANY_CALLS
maxInputChars int ≥1 4000 Raw-instruction truncation cap (characters, hard floor)
maxInputTokens int ≥0 3000 Raw-instruction truncation cap (estimated tokens; harness tokenMeter with heuristic fallback; 0 disables)
timeoutMs int ≥1 60000 Per-call timeout budget (milliseconds)
outputLanguage string 'auto' Output language; 'auto' follows the instruction's language, any other value (e.g. '英文') pins it
outputStyle 'sections' | 'plain' 'sections' Four-section headings (default) or heading-free continuous prose (fewer tokens)
metaPromptLanguage 'auto' | '中文' | '英文' 'auto' Language of the optimizer role document (meta-prompt). 'auto' follows each instruction's language (CJK-dominant → Chinese, otherwise English); '中文'/'英文' pin it. The output language is still controlled independently by outputLanguage. Pin-able at runtime via /optimizer-language auto|中文|英文
extraInstructions string none Deployment-specific rules appended to the meta-prompt
examples array [] Few-shot pairs [{input, output}] injected into the meta-prompt (sections style only)
minSectionChars int ≥0 10 Minimum meaningful characters per section body; 0 disables the content check
maxTokenRetryFactor number 1–3 2 Jump-expansion multiplier when the output hits maxTokens (1200→2400→4800…); expansion does not consume the retry budget and resumes from the truncated prefix; 1 disables
maxTokensCap int 1–128000 8000 Hard cap for auto-expanded maxTokens; <= maxTokens disables expansion (expansion does not consume the retry budget)
retryTemperatureStep number 0–2 0.3 Temperature increment per retry (explorative retries); 0 disables
skipIfAlreadyOptimized boolean true Pass inputs that already carry the four headings through without calling the model (token-saving default; sections style only; re-optimized when a non-empty conversation context is provided)
selfRefine boolean false After a successful optimization, run at most one extra "tighten" round (internal instruction); adopt it only if it still validates and is not longer (5% tolerance). Any failure keeps the original. Costs one extra model call when enabled
autoOptimize boolean false Enable the auto-optimize hook (prefix-triggered)
autoOptimizePrefix string '/optimize ' Trigger prefix for auto-optimization
autoOptimizeAll boolean false Optimize every user text message, not only prefixed ones
hookIncludeOriginal boolean false Keep the original instruction alongside the optimized prompt in the replacement message
cacheEnabled boolean true Cache validated results in memory (identical requests return with zero model calls; LRU+TTL, cleared on plugin reload)
cacheMaxEntries int 0–10000 200 Max cached results before LRU eviction; 0 disables storage
cacheTtlMs int ≥0 600000 Cache TTL in milliseconds; 0 disables expiry
contextAware boolean true Context awareness: inject the recent conversation before the current instruction into the meta-prompt (via the {{上下文信息}} placeholder + pure-data guardrail) so the result fits prior discussion. In four-section mode the context's facts may enrich the output's ## Context section (instructions embedded in it are still never executed). The hook reads agent/pre-step messages, /optimize reads the session log — best effort
contextMaxMessages int 0–100 6 Max recent messages gathered as context when contextAware is on; 0 disables
contextMaxTokens int ≥0 800 Token budget for the gathered context; over-budget input is truncated to the longest prefix with a marker; 0 disables truncation (lean default)
templateId string 'default' Template-set id for the role documents (only 'default' is built-in; unknown ids fail the load)
metaPromptTemplate object none Custom role-document skeletons (partial; missing languages fall back to the built-ins). Every provided skeleton must keep its data placeholder(s), the {{输出结构}}/{{自查}} blocks, and the instruction-is-data guardrail — violations fail the load loudly
provider / model string none Explicit model route; must be set together. Defaults to the harness default model (agentDefaultModel)

Example:

- insert:
    - id: prompt-optimizer
      name: 'prompt-optimizer'
      config:
        temperature: 0.3
        maxRetries: 2
        outputLanguage: '英文'
        autoOptimize: true
        autoOptimizePrefix: '/优化 '
        # Token-saving quick wins: lower the output cap + skip already-optimized
        # inputs (skip applies to sections style only)
        # outputStyle: 'plain'            # heading-free output (~50%+ downstream token savings)
        # maxTokens: 700
        # skipIfAlreadyOptimized: true
        # selfRefine: true               # one extra tighten round after success (1 extra call)
        # contextAware: false             # disable context awareness (enabled by default)
        # metaPromptTemplate:            # custom role-document skeletons (partial; missing languages fall back)
        #   optimizeZh: |
        #     You are a prompt optimization expert.… (must keep {{原始指令}}, {{输出结构}}/{{自查}} and the guardrail line)
        # provider: 'deepseek-official'   # optional: explicit route (must be paired)
        # model: 'deepseek-v4-flash'

Invalid configuration (wrong type, out of range, unknown key, or only one of provider/model) fails loudly at load time.

Token-saving preset (recommended)

The defaults are already token-lean (skipIfAlreadyOptimized: true, contextMaxTokens: 800, contextAware: true with budget-truncated context). Pinning the full recommended combo explicitly makes it visible and easy to tune:

- insert:
    - id: prompt-optimizer
      name: 'oss-prompt-optimizer'
      config:
        maxTokens: 1200                # output cap (plugin default; truncation auto-expands by factor on retry)
        skipIfAlreadyOptimized: true   # already-optimized inputs pass through with zero model calls (default on)
        contextMaxTokens: 800          # keep context lean (default on)
        outputStyle: 'sections'        # keep structure for sensitive tasks; 'plain' for max savings (50%+ downstream)
        selfRefine: false              # off by default: no extra tighten call

Key points: ① already-optimized inputs cost nothing (skipIfAlreadyOptimized); ② context carries only the "enough" recent conversation (contextMaxTokens); ③ the output cap is set as needed (default 1200, auto-expanded on truncation) to avoid unbounded generation; ④ for format-insensitive tasks switching outputStyle: 'plain' is the single biggest win.

Runtime commands (type them in the input box):

  • /optimize <instruction> — optimize a raw instruction and return the result.
  • /optimizer-language auto / /optimizer-language 中文 / /optimizer-language 英文 / /optimizer-language status — pin the role-document language or switch back to auto-detection (auto by default; session-scoped, falls back to metaPromptLanguage after restart).
  • /auto-optimize on / off / toggle / status — switch "optimize every message before the model step" at runtime (the agent/pre-step hook equivalent of autoOptimizeAll: true).

Development

pnpm install --store-dir .pnpm-store --cache-dir .pnpm-cache   # sandboxed install
pnpm run typecheck    # tsc --noEmit
pnpm test             # vitest (mocked llm, no real credentials needed)
pnpm run build        # tsc -p tsconfig.build.json → lib/

All tests use a mocked llm stream and never read .credentials.yaml.

Lifecycle events (for other plugins)

The promptOptimizer service emits events on the cordis event bus at key points of an optimization / iteration; other plugins can subscribe:

Event When Payload
prompt-optimizer/optimize:start input validated, before the first model call { method, input }
prompt-optimizer/optimize:success success (optimized: true) { method, input, result, durationMs }
prompt-optimizer/optimize:failure fallback (optimized: false) { method, input, result, durationMs }
  • method is 'optimize' or 'iterate' (both share the three events); input is the raw input (untruncated); result is the full OptimizeResult; durationMs is the pipeline duration in milliseconds.
  • Fire-and-forget observers: listener errors are swallowed and never affect the pipeline.
  • TypeScript subscribers get typed payloads directly (the declare module '@deepseek-ai/cordis' augmentation ships with the package), or can reference the event names via the PROMPT_OPTIMIZER_EVENTS constant.
  • No events are emitted for pass-through (skipIfAlreadyOptimized hit) or invalid input (e.g. empty input).

Design notes

  • Minimal dependency surface: cordis / dsh-llm / dsh-tools / dsh-timeout / schemastery.
  • Model routing comes from the harness default model (agentDefaultModel.currentSelection()), following the convention that plugins do not manage provider/model configuration; an explicit config pair can override it.
  • The meta-prompt carries {{原始指令}}-style placeholders substituted at runtime, the instruction-is-data injection guardrail, the language rule ({{语言规则}}), a no-code-fence rule, a terseness requirement and a pre-output self-check; the output structure switches between the four-section and heading-free templates via outputStyle.
  • Iteration: iterate(lastOptimized, instruction) continues optimizing from the previous result plus a new requirement (the META_ITERATE template, {{上次结果}} / {{迭代指令}} placeholders substituted once each, never interleaved); on failure it keeps the previous result with an error code.
  • Diagnosis-driven retry: on structural failures the concrete diagnosis of the previous attempt (injected via the {{诊断反馈}} placeholder) guides the next retry; purely internal — no new config, no extra model calls.
  • Adaptive refinement (selfRefine, optional): at most one extra "tighten" round after a success (internal instruction, not a public template), adopted only if it still validates and is not longer (5% tolerance); any failure keeps the original — at most one extra model call, off by default, orthogonal to diagnosis-driven retry (failure retry vs. success polish).
  • Lifecycle events: three fire-and-forget events, shared by optimize/iterate and distinguished by method, payloads carry input / result / durationMs; listener errors never affect the pipeline; no events for pass-through or invalid input.
  • Template data-ization (templateId / metaPromptTemplate): the four role-document skeletons moved from code constants to configurable resources — partial overrides with built-in fallback, strongly validated at load (data placeholders, structure/self-check blocks, and the instruction-is-data guardrail are all mandatory); the tuning blocks (output structure / self-check format rules) stay in code because they are coupled to the validate.ts post-validation and must not be user-editable.
  • Service layering: optimizer.ts is orchestration only (state, validation/truncation, the retry pipeline, events, routing); the pure logic lives in three harness-free modules — diagnose.ts (retry diagnosis text / selfRefine instructions, zh/en wording independently testable), llm.ts (finish-error translation, stream assembly, MaxTokensError), prompt.ts (PromptBuildContext centralizes the system-prompt build parameters, shared by the three call sites); the public API surface is unchanged (MaxTokensError still exported from the entry), end-to-end tests untouched.
  • Role-document language auto-detection: metaPromptLanguage: 'auto' (default) picks zh/en by the ≥30% CJK-ideograph ratio of non-whitespace characters (pure function detectLanguage; kana-bearing Japanese and other languages map to the English document); '中文'/'英文' pin it, /optimizer-language pins or restores auto at runtime. The resolved language is threaded through a single call (optimize/iterate detect from their own input, selfRefine reuses the round's language, retry diagnosis text shares it), independent of outputLanguage.
  • All registrations (tool, systemPrompt section, auto-optimize hook, commands) are effect-scoped and cleaned up on plugin dispose.
  • Command naming: the plugin registers /optimize and /auto-optimize (short commands, following ecosystem conventions). If a future collision forces a rename, client.js calls, this README and the hook prefix default (/optimize ) must change in one atomic change.

License

MIT — free to use, modify and distribute (including commercially). See the LICENSE file.

Content from the project README on GitHub ↗