dsh-skill-bundle
August 30, 2026 · View on GitHub
A plugin for DeepSeek Harness
that groups related skills into bundle boxes. Each box has one root
skill and any number of sub-skills; a box loads either by frontmatter
metadata (loadSubskills) or by letting the model decide from the body text.
It ships as a self-activating Harness bundle: installing the npm package adds a patch layer that inserts the plugin row, so no manual patching or symlinking is needed.
Why it exists
Harness already lets a model load skills on demand, which works well for a few unrelated skills. A family of related skills — a toolchain, a phased workflow, a project with several sub-tasks — has a real cost under the naive approach:
Every installed skill contributes a model-invocable name and description to the
<available_skills>catalog on each relevant prompt. A group installed as flat siblings therefore expands that catalog (and the prompt budget it consumes) even when the current task never touches the group. A loaded skill body only enters context after a tool or slash invocation, but the summaries of every sibling are always present — wasting tokens, bloating the context window, breaking KV-cache reuse, and slowing every turn.
Two symptoms follow:
- Context pollution — unrelated groups still consume prompt budget through their catalog summaries.
- Unnecessary questions — with many sibling summaries in-context, the model drifts into asking which piece to use instead of just working.
dsh-skill-bundle makes loading explicit and on-demand:
- A group (a bundle box) is exposed as one small root skill.
- Its sub-skills are not catalogued up front. They load only when the box
is pulled in via
loadSubskills— and then only the ones listed. - Nothing from the group is in context until you ask for it.
You can opt out per box: without loadSubskills, the box returns its short root
body and the model follows that text, fine for boxes you actually want
always-present.
Example — a "deploy" bundle
Three steps per deploy: build, push, rollback. Wrap them in one box:
boxes/deploy/
SKILL.md # root skill, frontmatter has `loadSubskills`
build/SKILL.md
push/SKILL.md
rollback/SKILL.md
With loadSubskills, telling the model to use deploy loads all three
sub-skill bodies at once — it immediately has the build/push/rollback
instructions and can run the whole deploy without asking "which one?".
Without it, deploy returns just its short body text and the model reads and
follows that.
Features
-
Bundle box — a directory with a root
SKILL.mdplus sibling sub-skill directories, each with its ownSKILL.md. -
Two load rules, decided by the root skill's frontmatter:
Root frontmatter On load the box produces loadSubskills: trueevery sub-skill body, in directory order (root body ignored) loadSubskills: [a, b]sub-skill a+bbodies only, in that order (root body ignored)no loadSubskillsroot body as-is; model decides from its text -
Each expanded sub-skill keeps its own identity and resource directory: the body is rendered as a
skill_contentblock whose resource base is the child's own directory, so relative paths inside it resolve correctly. -
Invalid
loadSubskillsvalues fail loudly (wrong type, unknown child name, or a duplicate name) instead of silently producing partial content. -
Model-facing tools:
skill_browse(list a box's sub-skill names — summaries only),skill_load(load one or more skills by exact name), plus the standardskilltool for root invocation. -
No framework changes — a plain Cordis plugin.
Requirements
- A working DeepSeek Harness installation (
dsh), e.g.dsh --profile web.
Install
The package is a Harness bundle: it declares dsh.bundle and ships a
prebuilt lib/ plus the default boxes/, so no build toolchain is needed on
the installing machine.
dsh plugin --profile web add @lihuu/dsh-skill-bundle
dsh --profile web --dump-config
The second command must show a dsh-skill-bundle layer. A running profile must
be restarted after bundle membership changes.
Using your own boxes
By default the plugin resolves boxes beside its installed package (boxes/).
To point it at your own directory, add a later patch row (profile or home
level) that replaces the inserted row with the same id/name and an
absolute boxesDir:
- insert:
- id: dsh-skill-bundle
name: '@lihuu/dsh-skill-bundle'
config:
boxesDir: /absolute/path/to/your/boxes
Harness patch rows replace complete config values instead of deep-merging them, so re-state every field you need.
Building from source (developers)
npm install # installs typescript + dev dependencies
npm run build # tsc compiles src/ -> lib/
npm test # build + node --test tests/*.test.mjs
lib/ and node_modules/ are git-ignored; they are rebuilt, not committed.
Activation semantics
- Each box contributes one model-invocable root candidate.
- Child candidates stay hidden from the model catalog (
modelInvocable: false) and remain directly user-invocable through Harness slash invocation. - A root with no
loadSubskillsreturns its own body unchanged. loadSubskills: trueexpands every immediate child in deterministic (directory-name) order.loadSubskills: [name, ...]expands exactly those children in the declared order and ignores the root body.- Children do not become new model-visible catalog rows after activation;
skill_loadis the model tool for exact hidden-child loading.
Creating a skill bundle
A box is just a directory. For example the shipped dsh-skill-bundle-guides box:
boxes/
dsh-skill-bundle-guides/
SKILL.md # root skill
install-plugin/SKILL.md
create-bundle/SKILL.md
bundle-from-skills/SKILL.md
fix-frontmatter/SKILL.md
Every SKILL.md needs name + description in its frontmatter:
---
name: dsh-skill-bundle-guides
description: "Guides for using this plugin."
loadSubskills: # optional: auto-load these sub-skills
- install-plugin
- create-bundle
- bundle-from-skills
- fix-frontmatter
---
Body text (ignored when loadSubskills is present).
Sub-skills are ordinary skills too:
---
name: install-plugin
description: "How to install dsh-skill-bundle."
---
How to install the plugin...
YAML gotcha
YAML 1.2 (used by this plugin) rejects a plain scalar that looks like a
"compact mapping". If a value — e.g. description — contains a comma next to a
colon (a: x, b, c), wrap it in double quotes:
description: "a, b, c: needs quoting because of the comma/colon"