← Blog
Aug 18, 2026 · 4 min read · verified against dsh v0.1.0-rc.7

How to Create Your First DeepSeek Harness Preset

A hands-on walkthrough of the dsh preset format, matched line-for-line to the official built-in presets — persona rows, tool packages, groups, and how to verify it works.

A DeepSeek Harness preset is a directory with two small YAML files. This walkthrough creates a read-only code reviewer from scratch — shell access hard-disabled, a reviewer persona wired in — and every snippet below is matched to the format of the official built-in presets, not paraphrased from memory.

What a preset actually is

<your-preset-id>/
├── agent.cordis.yml   # the composition — required, this file IS the preset
└── preset.yml         # display metadata for the preset picker (name/description/order)

Two things the design notes make official:

The iron rule is now official. Shipped presets refuse writes and deletes — the deployment’s copy is the reference a broken local preset gets compared against. The supported authoring path is, verbatim from the notes, “duplicate, then edit.”

Step 1 — run dsh

npx @deepseek-ai/dsh web   # UI at http://127.0.0.1:3080

Not sure what plugin rows your build actually boots? This prints the composed tree, and any row it prints can be replaced by a patch of your own:

dsh --profile web --dump-config

Step 2 — duplicate a built-in preset

Duplicate standard (or minimal if you want the smallest baseline) into your preset root under the dsh home — community docs document $DSH_HOME/.agent-presets/ — under a new id: my-reviewer. The roster discovers user-authored presets by filesystem scan.

Step 3 — write the display metadata

preset.yml is what the picker renders. The built-ins themselves use it exactly like this (yes, with Chinese display names):

name: My Read-Only Reviewer
description: Reads everything, executes nothing. Findings sorted by severity.
order: 10

order sorts the picker — the built-ins take 1–4, so start yours at 10.

Step 4 — wire the composition

agent.cordis.yml is a list of rows. Each row has a local id, a name pointing at the real package, and optional config. Three shapes cover almost everything:

The persona row — identity is a composable package, not a prompt string:

- id: persona
  name: '@deepseek-ai/dsh-persona'
  config:
    text: |-
      You are a staff-level code reviewer. You never run or modify code.
      Report findings as BLOCKER / MAJOR / MINOR / NIT, each with file:line
      and a concrete suggested fix.

Tool rows — mount only what the agent should see:

- id: tool-fs
  name: '@deepseek-ai/dsh-tool-fs'

- id: tool-fs-search
  name: '@deepseek-ai/dsh-tool-fs-search'

Disabling is structural. disabled: true unmounts the row for this preset — the tool isn’t suggested away, it isn’t there. A reviewer that must not execute anything:

- id: tool-bash
  name: '@deepseek-ai/dsh-tool-bash'
  disabled: true

The real package vocabulary you’ll reach for most: dsh-tool-fs, dsh-tool-fs-search, dsh-tool-str-replace-editor, dsh-tool-bash, dsh-tool-web (config includes fetch and searchTimeoutMs), dsh-tool-skill, dsh-tool-goal, dsh-tool-todo, dsh-tool-ask-user, dsh-tool-jobs, dsh-persona, dsh-agent-instructions. The built-in standard/agent.cordis.yml is the authoritative catalog — read it before inventing ids.

Step 5 — groups, when you need them

Some capabilities come as groups: a cordis:group row with an isolate realm and child rows. You’ll meet this for plan mode and delegation:

- id: planning
  name: cordis:group
  group: true
  isolate:
    planMode: true
  config:
    - id: plan-mode
      name: '@deepseek-ai/dsh-plan-mode'

The realm rules are enforced, not advisory: a service row outside a group publishes into the process-global root realm and the mount rejects it rather than letting two sessions collide. When you duplicate a built-in, keep its groups intact.

Step 6 — verify

Open a new session and pick your preset. Then test the edges, not the happy path:

Two switching rules from the design notes: you can only pick a preset while the session is still blank (once a turn has run, the history was produced under that tool set and the host refuses the swap), and a resumed session rebuilds the composition it was created with — the preset id rides the session header. Edits to your files apply to the next session, never the running one.

If a package name rejects, you’ve probably hit a typo or a renamed package — --dump-config lists what actually resolves in your build.

Steal from the best

The authoritative example is the official standard/agent.cordis.yml — duplicate it, keep its groups intact, and trim rows until the toolset says exactly what your agent should be. (A curated community catalog lives on this site too; it returns once every preset is verified against a live install.)