Runtime-switchable model output styles with Claude Code outputStyles parity, plus the output.render.* presentation protocol: a /style command, per-session persistence, systemPrompt injection, six built-in styles, a web picker, and a renderer registry with per-session/per-tool rules and /export.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-output-styles
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:PerryLink/dsh-output-styles
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
dsh-output-styles
Switch the model's output style at runtime — one /style concise and every reply from now on is terse.
The Claude Code outputStyles equivalent for DeepSeek Harness: styles are Markdown files, the choice is persisted per session, and the body is injected at every prompt assembly.
Install · Configuration · FAQ · 简体中文 · English · Español · Português · हिन्दी
dsh plugin --profile <name> add dsh-output-styles
Community plugin, not affiliated with DeepSeek. Canonical repository: https://github.com/PerryLink/dsh-output-styles (Gitee is a mirror).
What it is
/style concise — and every reply from now on is terse; /style off — back to the project default. dsh-output-styles brings Claude Code's outputStyles to DeepSeek Harness: a style is a Markdown file (frontmatter for metadata, body as the model directive), six built-ins ship in the box, the choice lives in the output_style storage domain keyed by sessionId and survives restarts, and the current body is injected into the system prompt at every assembly, truncated at a configurable budget. Styles apply to the main conversation only; subagent sessions keep their own prompts (matching Claude Code).
How it works
flowchart LR
U[You type /style concise] --> C[command registry]
C -->|command/run logged| L[(session log)]
C -->|put {style, source}| D[(output_style domain)]
D --> R[OutputStyleRuntime]
R -->|body at every assembly| S[systemPrompt section order 90]
S --> M[Model request]
M -->|full system prompt| H[system/message logged]
Everything the model sees is reconstructable from the session log — no new session event type, no agent-loop changes. The style name comes from command/run, the exact injected text from system/message, and the provenance marker { kind: 'dsh-output-styles' } rides in the domain record.
Demo
You > /style
output style off
concise — Terse, direct answers — minimal prose, no preamble. (Daily coding work, tool-heavy sessions, or when prompt length matters.)
explanatory — Educational answers with short "Insights" that teach as you work. (Learning a codebase, onboarding, …)
formal — Formal, precise prose with complete sentences and defined terms. (Reports, documentation, release notes, …)
learning — Collaborative learn-by-doing mode with short "Insights" and small hands-on steps for the user. (Pairing, onboarding, …)
proactive — Execute immediately, assume reasonable defaults, and prefer action over planning. (Routine multi-step work, …)
step-by-step — Numbered reasoning steps with explicit intermediate results. (Debugging, design decisions, …)
You > /style concise
switched to concise
You > 请只用一句话介绍你自己。
AI > 我是运行在 DeepSeek Harness 插件化平台上、基于 deepseek-v4-pro 模型的 AI 编码代理。
Three-step setup
Install (pick one channel):
# git channel (latest main; the prepare script builds with production dependencies only) dsh plugin --profile web add github:PerryLink/dsh-output-styles # npm channel (published releases) dsh plugin --profile web add dsh-output-styles # tarball channel: pnpm pack in this repo, then dsh plugin --profile web add ./dsh-output-styles-<version>.tgzMinimal config (sane defaults; set the project default for new sessions as needed):
- insert: - id: output-styles name: dsh-output-styles config: defaultStyle: conciseVerify it works:
dsh --profile web --dump-config | grep -A3 'id: output-styles'> /style output style off concise — Terse, direct answers — minimal prose, no preamble. (…) > /style concise switched to concise
Uninstall: dsh plugin --profile web remove dsh-output-styles (or remove the row from the profile patch).
Every PerryLink plugin is listed in the built-in DSH Desktop market: Market → Sources → Add source → paste https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json → select. Installation still requires the market's npm identity check and your confirmation.
Core capabilities
dsh-output-styles is the Claude Code outputStyles equivalent for DeepSeek Harness: a /style command that switches the model's output style at runtime, persists it per session, and injects it at every prompt assembly.
- Style library — one Markdown file per style (
styles/*.md); frontmatter for metadata, body = the model directive. Six built-ins ship in the box (concise,explanatory,formal,learning,proactive,step-by-step), including Claude Code-parityproactiveandlearning. /stylecommand — no argument lists styles (with descriptions) plus the current selection;/style <name>switches;/style offrestores the project default.- Session-scoped persistence — the choice lives in the
output_stylestorage domain, keyed by sessionId, and survives restarts. - System-prompt injection — a
systemPrompt.section()contribution (ordersectionOrder) injects the current session's style body at every assembly, truncated at a configurable budget. - Claude Code parity —
keep-coding-instructions,force-for-plugin(forcealias),outputStylesJSON compatibility, layeredstylesDirdirectories, hot reload, and a project-default fallback that is live-editable from the Web Plugins page. - Renderer registry (
output.render.*) —ctx.outputRendererslets any plugin register a pure presenter, applied through theoutput.render/beforewaterfall; built-in renderersconciseandstep-by-step. - Per-session/per-tool rules —
rules: [{ match: { tool: 'bash' }, style: 'concise' }]name the renderer for matching requests; live-editable from the Web Plugins page. /transcript— render the current session to Markdown or sanitized HTML through the render pipeline;--save <path>writes the sanitized document to that workspace path after user approval. Every render keeps the original text beside the rendered one.
Commands & tools surface
| Surface | Kind | Notes |
|---|---|---|
/style |
command | List styles, switch, or restore the project default |
/transcript |
command | Render the current session to Markdown or sanitized HTML; --save writes with approval |
output_style |
storage domain | Session-scoped style choice, keyed by sessionId |
systemPrompt.section() |
contribution | Injects the current style body at every assembly |
output.render.* |
renderer registry | ctx.outputRenderers + the output.render/before waterfall |
style |
projection | { options, currentValue } folded from settled commands |
| Web picker | client entry | dsh-output-styles/client decorates /style with a popup picker |
Command reference
| Input | Outcome |
|---|---|
/style |
List the current selection + one line per style (name — description) |
/style concise |
Switch (durable write), switched to concise |
/style Diagrams first |
Multi-word names are the whole remainder |
/style off |
Restore the project default (settings default, then defaultStyle) |
/style nope |
error: unknown output style "nope" (available: …) |
/transcript |
Render the current session to Markdown through the renderer pipeline |
/transcript md |
Render to Markdown (md is the shorthand for markdown) |
/transcript html |
Render to sanitized HTML |
/transcript --renderer=concise |
Render with one renderer forced (rules bypassed) |
/transcript md --save report.md |
Render, then write the sanitized document to report.md after approval |
Examples
These are the plugin's real entry points; install once, then call one.
Detailed example — command /style concise, output:
switched to concise
Observed from plugins/dsh-output-styles/src/runtime.ts:474-478; asserted in tests/runtime.spec.ts:91; README §Command reference line 194
Failure branch — input /style nope, output:
unknown output style "nope" (available: concise, explanatory, formal, learning, proactive, step-by-step)
Observed from plugins/dsh-output-styles/src/runtime.ts:213-214,474-475; plugins/dsh-output-styles/tests/runtime.spec.ts:118-125
Brief examples: /transcript · output_style
Style library
One Markdown file per style; frontmatter for metadata, body = the model directive. name defaults to the file name and may contain spaces (Diagrams first).
| Field | Default | Meaning |
|---|---|---|
name |
file name | Switch target; letters, digits, spaces, and hyphens (off is reserved) |
description |
— (required) | One sentence shown in listings and the picker |
whenToUse |
— | Optional guidance appended to listings |
keep-coding-instructions |
false |
Keep the harness prompt when true; replace it when false (Claude Code semantics) |
force-for-plugin |
false |
Apply unconditionally, overriding any session selection; force is an alias, at most one style may set it |
With compatJson: true, Claude Code outputStyles JSON entries ({ name, description, prompt }) load beside Markdown styles; unparseable entries are skipped with a warning.
Renderer protocol
The output.render.* protocol turns presentation into an extension point. A renderer is a pure presenter — presenter(text, context) maps args to display data, never touches the DOM — matched by tool name and content type, ordered by priority.
- Waterfall first: every render request passes through
output.render/before({ text, context }); listeners must callnext(). - Rules:
rules: [{ match: { tool: 'bash' }, style: 'concise' }]names the renderer for matching requests; ties break bypriority, then rule order. - Built-ins:
concise(whitespace compaction + budget truncation) andstep-by-step(consistent step numbering). - Auditability: every render result carries
{ original, rendered, rendererId, changed }; the rendered text is what surfaces, the original stays reconstructable from the session log.
Web picker
The dsh.client entry decorates the host /style command's bare invocation with a popup picker: an "off" row plus one row per library style (description · whenToUse), the active row marked. Picking submits /style <name> through the command Remote, so every switch keeps the host's durable command lifecycle. The picker follows the Web UI's shipped zh/en locale pair.
Configuration
All tunables are Schemastery Config fields (changeable from cordis.yml). Invalid values fail the load.
defaultStyle and rules are declared volatile(), so on a host that composes the settings forms seam they are live, Web-editable fields: edit them under Plugins → dsh-output-styles in the Web UI and the running plugin adopts the new value without a remount. Every other field stays composition-only and needs a reload. A committed value is validated before it is applied — an invalid one is rejected and the running values are left unchanged.
Most-used keys:
| Key | Default | Web-editable | Meaning |
|---|---|---|---|
stylesDir |
[] |
no | Style-library directories, resolved against cwd; later entries override earlier ones. [] = the bundled styles/ only |
maxStyleChars |
4000 |
no | Style-body budget (≥ 1); longer bodies are truncated with a marker |
defaultStyle |
'' |
yes | Style for sessions that never selected one; '' = no style. A name that is not in the live library is refused rather than injected |
compatJson |
true |
no | Load Claude Code outputStyles JSON entries (single objects or arrays) |
sectionOrder |
90 |
no | Order of the injected section (0 = persona, 100–199 = tool guidance) |
includeBuiltins |
true |
no | Include the package's bundled styles/ as the lowest-priority layer |
watchStyles |
true |
no | Reload the library when a style file changes on disk |
rules |
[] |
yes | Per-session/per-tool render rules: [{ match: { tool?, contentType?, session? }, style, priority? }] |
| Key | Default | Web-editable | Meaning |
|---|---|---|---|
stylesDir |
[] |
no | Style-library directories, resolved against cwd; later entries override earlier ones. [] = the bundled styles/ only |
maxStyleChars |
4000 |
no | Style-body budget (≥ 1); longer bodies are truncated with a marker |
defaultStyle |
'' |
yes | Style for sessions that never selected one; '' = no style. A name that is not in the live library is refused rather than injected |
compatJson |
true |
no | Load Claude Code outputStyles JSON entries (single objects or arrays) |
sectionOrder |
90 |
no | Order of the injected section (0 = persona, 100–199 = tool guidance) |
truncationMarker |
"\n\n[style truncated]" |
no | Marker appended at the truncation point |
includeBuiltins |
true |
no | Include the package's bundled styles/ as the lowest-priority layer |
watchStyles |
true |
no | Reload the library when a style file changes on disk |
rules |
[] |
yes | Per-session/per-tool render rules: [{ match: { tool?, contentType?, session? }, style, priority? }] |
enableExport |
true |
no | Register the /transcript command (Markdown/HTML session export, renderer-aware; --save writes with approval) |
respectCoreOutputStyles |
true |
no | When a core outputStyles service is detected, skip this plugin's prompt injection (keep hot-switch / rules / export) |
Profile patch override example:
- insert:
- id: output-styles
name: dsh-output-styles
config:
defaultStyle: concise
rules:
- match: { tool: bash }
style: concise
Migrating from 0.1.6 or earlier. Those versions kept two plugin-owned settings sections, output-style (style) and output-style-rules (rules). The 0.1.7 host deleted the API they were built on, so both sections are gone. Move any value you had there into the corresponding Config field — output-style.style → defaultStyle, output-style-rules.rules → rules — in your profile patch (cordis.yml), or set it on the Plugins page. Per-session choices made with /style are unaffected: they live in the output_style storage domain.
FAQ
The choice lives in the output_style storage domain, keyed by sessionId, and survives restarts. No other state is persisted and no network requests are made. Style bodies come from the styles/ directories and stylesDir; they are re-read at every assembly (with watchStyles on, they also hot-reload).
defaultStyle and rules are volatile() fields: they are live, Web-editable fields only when the host composes the settings forms seam. Otherwise they stay composition-only and need a profile reload. A committed value is validated — an invalid name is rejected and the running values are left unchanged.
There is no silent conflict. When the plugin detects a core outputStyles service it degrades to the incremental surface (hot-switch / rules / /transcript) and leaves prompt injection to the core; every /style and /style off reply carries a degraded coexistence mode note, and the style session projection exposes promptInjection: 'enabled' | 'disabled', so no surface implies the style is in force when it is not. The full argument is in docs/COEXISTENCE.md.
No network requests, no model calls. The plugin observes system-prompt/assemble and folds the current style body (truncated at maxStyleChars, 4000 characters by default) into the system prompt; every listener delegates through next(), so the chain is never short-circuited.
No. Styles apply to the main conversation; subagent sessions keep their own prompts, matching Claude Code.
Security boundaries
- Public services only. Contributes
systemPrompt, commands, storage, and settings; no engine / agent-loop / apiproxy / official-UI changes. - Model-visible ⟺ logged. Everything the model sees is reconstructable from the session log — no new session event type, no agent-loop changes.
- Original always kept. Every render (and
/transcript) keeps the original text beside the rendered one; sanitized HTML is used for HTML export. - Disk writes gated.
/transcript --savewrites only after the approval service grants it, and the written content passes through thesanitizeTextpure function first; without an approval or fs service it writes nothing (fail-closed).
Permissions & data
- Permissions: declares
fs:read,fs:write,fs:watch,storage:read,storage:write, andsettings:readin its workshop manifest. - Data: the style choice lives in the
output_stylestorage domain (keyed by sessionId); no other state is persisted, no network requests. - Session log: the style name comes from
command/run, the exact injected text fromsystem/message; the provenance marker{ kind: 'dsh-output-styles' }rides in the domain record.
Known limitations
- Core coexistence. If a first-party
outputStylescapability lands, this plugin detects itsoutputStylesservice and degrades to the incremental surface (hot-switch, rules,/transcript) while leaving prompt injection to the core — seedocs/COEXISTENCE.mdand the exporteddetectCoreOutputStyles/coexistenceReportfunctions. Degraded mode is stated where you can see it: every/styleswitch and/style offreply carries adegraded coexistence modenote, and thestylesession projection exposespromptInjection: 'enabled' | 'disabled', so no surface implies the style is in force when it is not. (Before 0.7.0 the only trace was actx.logger.infoline, which never reaches the session transcript.) - Main conversation only. Styles apply to the main conversation; subagent sessions keep their own prompts (matching Claude Code).
- Truncation. Style bodies longer than
maxStyleCharsare truncated with a marker. - Skipped style files. A bad style file is skipped with a warning and never breaks the profile.
Interoperability with other DSH plugins
Verified against DSH 0.2.0-rc.2 (the runtime this README ships for) and the high-star plugin set surveyed on 2026-10-05.
This plugin does not interfere with other plugins, including the widely installed high-star ones:
- No tool-name collision. Every tool is namespaced; no bare name owned by a shipped tool or another plugin is registered.
- No service-key collision. It provides
outputRenderers; that key is not a built-in seam and is not provided by any surveyed high-star plugin. - No slot collision. It registers no client slot key, so it cannot contend for a
shadows-shipped-uiseat. - No HTTP route collision. It registers no
webServerprefix. - No patch-layer collision. The bundle patch only
inserts its own row; it never overrides a built-in row'sconfig. - No global mutation. It does not patch prototypes, rewrite
process.env, or replace the global fetch dispatcher.
Shared event listeners are non-interfering by construction. It observes the ordering-sensitive event system-prompt/assemble with ctx.on() — Cordis's broadcast registration, where every listener runs and none can starve another. Every listener here delegates through next(), so the chain is never short-circuited, and a mutation is applied to the value next() produced rather than returned in its place:
system-prompt/assemble— also used bydsh-routing-suite(7000★, 13 listeners).
Static evidence: dsh-plugin-doctor K10–K13 report pass for every check on this repository.
Conflict screening
Screened against the DSH ecosystem before development (2026-08 snapshot): no style/output-style repository under topic:dsh-plugin, no output-style category in the four major awesome lists, and no entry in the dsh-hub catalog. The closest neighbors — dsh-soul-md (persona) and dsh-claude-marketplace (output styles explicitly deferred to v0.2+) — are adjacent, not conflicting.
Compatibility
| Surface | Status |
|---|---|
| Harness | DeepSeek Harness dsh-v0.2.1-rc.2 (adapted 2026-09-09): the session envelope keeps its ignorable field for stored-log read compatibility only — Session.append still cannot stamp it, so audit-gate behavior is unchanged. Verified 2026-09-11 against the dsh-v0.2.1-rc.2 checkout (full gate chain + profile install smoke). |
| Node | ^22.19.0 || >=24.9.0 |
| Platforms | All (host + web client) |
| Model | Any (system-prompt injection) |
Differences from Claude Code
| Claude Code | dsh-output-styles | |
|---|---|---|
| Style files | .claude/output-styles at user/project/managed levels |
stylesDir directories + bundled styles/, later directory wins |
| Custom styles | Markdown, frontmatter name/description/keep-coding-instructions/force-for-plugin |
Same fields (force-for-plugin accepted verbatim, force as alias) + whenToUse |
| Legacy JSON | outputStyles array in settings.json |
Loaded verbatim (compatJson: true) |
| Taking effect | After /clear or a new session |
Immediately — the system prompt re-assembles per request |
| Subagents | Styles do not apply | Same — subagent sessions keep their own prompts |
| Switching | /config menu or outputStyle setting (the /output-style command was removed in v2.1.91) |
/style command + Web picker + the defaultStyle field on the Web Plugins page |
Ecosystem & quality
- Gitee mirror (sync mirror; GitHub is canonical)
- dshfind plugin page (installs and ranking)
- OpenSSF Scorecard report (driven by .github/workflows/scorecard.yml)
- dsh-plugin-doctor verification report (K10–K13 all
pass) - DSH Market (listed)
- 1024 store channel:
npm i -g dsh1024once, thendsh1024 plugin --profile web add dsh-output-styles(counts toward the deepseek1024.com install ranking) - Ecosystem knowledge base — measured data, not marketing: plugin development guide · plugin-selection data · maintenance criteria
Development
pnpm install
pnpm run typecheck # both tsc projects
pnpm test # vitest — 156 tests
pnpm run verify # typecheck + tests + self-contained (the prepublishOnly gate)
pnpm run check:readmes # five-language README consistency gate
pnpm run build # lib/ artifacts (host + client bundles)
pnpm pack # tarball for dsh plugin add
Releases: pushing a v* tag whose suffix matches the package.json version triggers the Publish workflow — full verification, then an npm publish with provenance.
The README images are generated programmatically by scripts/gen-readme-assets.py: the hero is a 1440×576 WebP drawn with Pillow (zh/en × light/dark) and the structure diagram is a hand-written 1100×660 SVG (light/dark). Re-running it overwrites docs/assets/dsh-output-styles-hero-* and docs/assets/dsh-output-styles-flow-v5-{light,dark}.svg. This repository carries no benchmark data, so there is no evidence chart.
Topics: deepseek-harness, dsh, dsh-plugin, output-style, output-styles, claude-code
Family & thanks
This project is part of the DeepSeek Harness plugin family from PerryLink (all Apache-2.0). If it is useful, a star helps the next person find it — nothing is gated behind it.
- @kskazuha — the Web style picker revival on 0.1.7 (#8).
- @Bingtang1019 — the module-table handoff report for
lib/client.js(#10). - @PerryLink — author and maintainer: plugin architecture, style library, bundle install, Web picker, five-language docs, and CI/release tooling.
License
LICENSE (Apache License 2.0) © 2026 dsh-output-styles contributors
Links
More in this category
yjh051108/dsh-routing-suite★ 7015
One repository, three parts: a runtime injector for DSH plugin packages (inject, hot-reload, unload, promote a dev staging tool to the front, route self-heal, plus a settings-page plugin manager that lists, unloads and drags folders in to internalize), a task-aware reasoning-mode router agent preset (router-standard / router-spec / router-react), and a graded two-level task protocol whose six tools (commit_star, lock_stage, revise_do, edit_plan, mark_task, redteam_verdict) pin task state to disk. The injector implementation ships in-tree, so the install carries its own behaviour rather than a dependency list.
strukto-ai/mirage#dsh★ 3783
Swaps the filesystem and bash providers for a mirage virtual workspace: file tools and shell commands run over mounted resources (RAM, S3, Redis, Slack, Gmail, Notion, Postgres) instead of the host disk, with per-mount read/write/exec modes, per-command sandbox routing (monty, pyodide, quickjs in process; docker, e2b, daytona remote), and installed CLIs (git, gh, slack, linear, ntn, gws, or one you register) as head words in the virtual terminal.
first-fluke/oh-my-agent#dsh★ 1440
Runs project OMA checks before and after tool execution and when a turn tries to finish. Installs its OMA CLI dependency and initializes unconfigured projects on first use. Requires Node.js 22+; initial setup needs network access and project write access.
MichengAI/dsh-skills-manager★ 552
Manages local DeepSeek Harness skills in Settings and shows shared Agent skills as read-only.
hust-open-atom-club/oh-dsh★ 324
Community distribution: TUI, desktop, and Web UI as one bundle with layered installation.
Fishquito7/dsh-skill-mcp-panel★ 272
Manages DSH skills and MCP servers from the web settings: skill cards with hot enable/disable, workspace scopes, groups, batch migration and drag-and-drop import, plus stdio/HTTP MCP CRUD with connection tests, secret redaction and the unified dsh-panel CLI.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.