dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH Web UI plugin: Skills tab with per-skill enable/disable toggles (DeepSeek Harness)
English | 简体中文
A DeepSeek Harness (DSH) plugin that adds a Skills tab to the web UI conversation view ring (next to Chat and Trajectory). It lists every available skill with a live search box and a per-skill enable / disable switch, plus a bulk enable-all / disable-all toggle.
Disabling a skill writes disable-model-invocation: true into the skill's SKILL.md frontmatter, so the model-facing skill catalog drops it while the user-facing surface keeps it — the skill stays visible in the list and can be re-enabled at any time.
conversation.view slot, alongside Chat and TrajectorySKILL.md (application-bundled skills such as office-docx / office-pptx / office-xlsx, and runtime-registered skills) render with a Built-in badge and no toggle, instead of failing with skill "…" not foundThe plugin ships two halves in one npm package:
| Half | Entry | What it does |
|---|---|---|
| Host | lib/index.js | Registers POST /api/skill-manager/toggle, …/packages, …/refresh, and …/status on the DSH web server, resolves the skill's SKILL.md through the registry and the disk (project roots first, then user roots), rewrites its frontmatter, and watches the user skill roots for new packages |
| Client | lib/client.js | Registers the Skills conversation-view tab, renders the list from the skills.list RPC, and calls the host routes on toggle, reload, and revision poll |
Disabling one skill (enabled: false) adds:
disable-model-invocation: true
to the skill frontmatter. The filesystem skill provider watches the skill roots, so the catalog invalidates automatically after the write — no restart needed for the catalog itself (a restart is required after installing or updating the plugin).
The core filesystem provider discovers one level deep, and this plugin's package
provider only watches containers it already knows, so a package installed as
<root>/<pkg>/skills/<name>/SKILL.md while DSH is running is observed by
neither watcher and would stay invisible until a restart. The host half
therefore:
$DSH_HOME/skills and $DSH_AGENTS_HOME/skills recursively and
invalidates the registry (plus bumps a revision) after each burst of events
(400 ms debounce);POST /api/skill-manager/refresh to invalidate on demand and
POST /api/skill-manager/status to read the revision; and409 read-only-skill and an
explanatory message, which the client turns into a permanent read-only row.Requires a DSH profile (the example uses the web profile):
# from the plugin checkout, install into the profile
dsh plugin --profile web add file:./dsh-plugin-skill-manager
# or directly with pnpm
pnpm --dir "$HOME/.dsh/profiles/web" add file:./dsh-plugin-skill-manager
Then add the package to the profile's bundle layers. dsh.profile.bundles in ~/.dsh/profiles/web/package.json should include:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-plugin-marketplace",
"dsh-plugin-skill-manager"
]
}
}
}
Finally restart DSH and refresh the web UI. A Skills tab appears next to Chat and Trajectory.
Note:
file:dependencies are copied into the pnpm store. After editing the plugin sources, re-runpnpm --dir "$HOME/.dsh/profiles/web" remove dsh-plugin-skill-manager && pnpm --dir "$HOME/.dsh/profiles/web" add --force file:./dsh-plugin-skill-managerand restart DSH.
pnpm --dir "$HOME/.dsh/profiles/web" remove dsh-plugin-skill-manager
Remove dsh-plugin-skill-manager from dsh.profile.bundles in ~/.dsh/profiles/web/package.json, then restart DSH. Skills disabled by the plugin keep their frontmatter; re-enable them with the plugin, or delete the disable-model-invocation line manually.
# syntax-check both halves
node --check lib/index.js
node --check lib/client.js
# verify the composed profile tree includes the plugin
dsh --profile web --dump-config | grep -A2 skill-manager
The three suites import the plugin through the profile's node_modules, so copy
them next to the profile and run them from there:
cp test/*.test.mjs "$HOME/.dsh/profiles/web/"
cd "$HOME/.dsh/profiles/web"
node package-scan.test.mjs # package containers, ranks, provider roots()/list()
node target-resolution.test.mjs # writable roots, read-only classification, toggle targets
node client-load.test.mjs # browser bundle loads and registers the Skills view
dsh-plugin-skill-manager/
├── package.json # dsh.bundle.patch + dsh.client declarations
├── cordis.patch.yml # loader row mounting the host plugin
├── test/ # standalone suites (see Tests)
└── lib/
├── index.js # host half: toggle/packages/refresh/status routes + root watcher
└── client.js # client half: Skills tab (__ModuleLoader__ bundle)
The host half also understands skill packages: a skill root may contain
package directories where the level-1 name is the installed skill-package name
and the level-2 skills directory holds the skill bundles:
~/.dsh/skills/
├── my-package/ # level 1: skill-package name
│ ├── .claude-plugin/plugin.json # optional manifest (Claude Code compatible)
│ └── skills/ # level 2: skills
│ ├── group/ # optional grouping layer (bounded depth)
│ │ └── some-skill/SKILL.md
│ └── other-skill/SKILL.md
└── flat-skill/SKILL.md # flat layout: also registered
Recognition. A package is identified by its manifest first —
.claude-plugin/plugin.json or dsh-plugin.json — whose skills array is the
authoritative list of skill directories; without a manifest, a package with a
skills/ subtree is discovered by a bounded walk (depth ≤ maxNestingDepth).
Both layouts are registered. Besides packaged skills, flat skills — a
level-1 directory holding SKILL.md directly, the shape produced by unzipping,
copying, or git clone-ing a skill — are registered as containers too, keeping
the root's own rank:
Set flatSkills: false to restore the package-only behaviour.
Effects. The host registers a packages skill provider (a
FileSystemSkillProvider subclass) whose scan roots are the package containers
and the flat skill directories: containers are ranked
root.rank - packageRankOffset, flat skills keep root.rank. The toggle route
locates packaged and flat skills alike.
UI. The Skills tab groups rows under their package: a collapsible package
header (mattpocock ▸) that expands to the package's skills, backed by
POST /api/skill-manager/packages; skills outside any package render
ungrouped. Searching auto-expands every group so matches stay visible.
Built-in skills are hidden by default. Rows the host cannot toggle —
application-bundled skills such as the office set and the bundled
docx/pdf/pptx/xlsx entries — stay out of the list. A Show built-in /
Hide built-in switch in the toolbar reveals them and reports how many are
currently hidden; once shown they keep their "Built-in (read-only)" badge. The
list therefore defaults to what can be acted on.
Config (cordis plugin config, e.g. via the loader patch row):
| Key | Default | Meaning |
|---|---|---|
enablePackages | true | Master switch for package scanning |
manifestFiles | ['.claude-plugin/plugin.json','dsh-plugin.json'] | Manifest file names, in priority order |
maxNestingDepth | 4 | Fallback walk depth under <pkg>/skills |
packageRankOffset | 10 | Rank delta so packaged skills beat flat skills of the same root |
flatSkills | true | Also register <root>/<skill>/SKILL.md flat skills as containers |
includeRoots | ['project-dsh','project-agents','custom','user-dsh','user-agents'] | Which base roots participate |
A DSH restart is required after updating the plugin; packaged and flat skills
then appear without further setup. The package-scan helpers are exported from
lib/index.js and covered by a standalone test (run from the profile dir:
node skill-pkg-test.mjs).
A missing row in the Skills tab is almost always a layout problem. Make sure the skill matches either shape:
~/.dsh/skills/<name>/SKILL.md # flat
~/.dsh/skills/<package>/skills/<name>/SKILL.md # packaged
The next most common causes, in order: a SKILL.md without name /
description frontmatter, a name that is not kebab-case
(^[a-z0-9]+(?:-[a-z0-9]+)*$), or frontmatter that fails YAML parsing (the host
logs skill file … ignored).
Mind line endings when validating that YAML: on Windows files are often CRLF, and locating the closing frontmatter with
\n---truncates the block and reports a false syntax error. Compare whole lines with the trailing\rstripped instead.