dsh-skill-mini-manager
Skill manager page in the DSH Web Settings panel, at the same level as Plugins: lists every skill the installation serves with its own summary, source and disk footprint, and removes any of them into a recoverable trash.
Install
dsh plugin --profile web add dsh-skill-mini-manager
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-skill-mini-manager · Skill Mini Manager
English | [中文](README.md)
One skill manager page inside DSH Web Settings: it lists every skill the
installation currently serves, shows each skill's own summary, and removes any
of them.
- Plugin id:
dsh-skill-mini-manager - English name: Skill Mini Manager
- Chinese name: skill 极简管理
It sits at the same level as the built-in 插件 (Plugins) section — its own
entry in the same tier of the Settings sidebar, not a tab inside Plugins.
Preview
What it shows
One row per skill:
Field · Source
Name · the skill frontmatter's name
Summary · the skill frontmatter's description — the routing text the skill itself wrote, shown verbatim
When to use · the optional whenToUse
Source · user-dsh / user-agents / project-dsh / project-agents / custom / bundled
Invocable on · model / user (disable-model-invocation, user-invocable)
Footprint · the skill directory's absolute path, file count and byte total
Served to · which presets (or the global layer) hand this skill to an agent
The top of the page carries a filter (name, summary or when-to-use), a refresh
action and a count; the trash sits at the bottom.
Which layer a skill lives in (important)
The DSH skill registry is host + per-scope layered: deployment-level
providers register into the global layer, while each agent preset's own
skill-filesystem row registers into that preset's layer. The catch is that
dsh-web-app's patch disables the host skill-filesystem row ("presets own
local discovery"), so on a stock web profile the global layer is empty and
every user skill lives in a preset layer — reading the global layer alone yields
an empty catalog.
So this plugin reads every view: the global layer plus each preset in the
roster, merged by name. A preset is read through the scope DSH provides for a
host reader with no agent, and both API generations are supported:
- DSH 0.2 and later:
acquireScope(id), which returns a lease
({ key } & AsyncDisposable). It must be disposed once the scoped read is
done, or that preset revision is retained in memory; this plugin releases it in
a finally, including when the read throws.
- DSH 0.1.x:
standingKeyFor(id), which returns the key directly and has
nothing to release.
A preset that cannot be read is reported on the page rather than silently
costing you rows.
Each skill therefore names the presets that serve it (e.g.
served to preset standard · preset code), and a skill from a preset-private
skills/ directory (such as the two the shipped cordis preset carries) names
only that preset. A name that resolves to different files in different
presets is listed but never offered for removal (ambiguous).
Everything comes from the host's live ctx.skills registry: every open or
refresh re-reads it with no caching, so the page shows what the model can load
right now.
How removal works
Remove opens a two-step confirm that names the exact path that will move.
On confirmation the host:
1. Moves the whole skill directory (or the single .md file of a flat skill)
to <DSH_HOME>/.skill-mini-manager/trash/<stamp>-<name>-<nonce>/payload,
writing a meta.json beside it that records the original path.
2. That location is outside every scanned skill root, so the filesystem
provider's one-level discovery can never see it again — the skill leaves the
model catalog immediately (the provider's own watcher invalidates).
3. The trash at the bottom of the page lists removed skills, each with a
Restore action that moves it back to its original path. Empty trash
is the only irreversible verb in the plugin, and it confirms first too.
What cannot be removed
Refusals are enforced on the host; the page shows the reason instead of a button.
Reason · Meaning
bundled-source · a DSH-bundled skill (DSH_BUNDLED_SKILL_DIR) — a distribution artifact, not user content
runtime-provider · registered by a plugin at runtime; it has no files
system-owned · inside the system-owned .system directory
not-on-disk · the definition carries no file path
outside-skill-roots · outside every known skill root
unsupported-layout · neither <name>/SKILL.md nor <name>.md
missing · the directory is there but SKILL.md is gone
ambiguous · the name resolves to different files in different preset layers; the page sends only a name, so there is no single target and it refuses to guess
Plus two structural guarantees:
- The browser never sends a path. The page sends a skill name (
remove) or
a trash entry id (restore/purge); the host re-derives the target from its
own rules every time. A compromised page cannot make this plugin delete an
arbitrary file.
- The removal target is never a skill root itself. A bare
SKILL.mdsitting
directly in a root is refused rather than taking the whole skills/ directory
with it.
Host interface
The host half mounts four exact routes on webServer. All of them accept
same-origin requests only (an Origin that disagrees with Host is a 403):
Method · Path · Effect
GET · /dsh-skill-mini-manager/api/catalog · skills + trash + root status
POST · /dsh-skill-mini-manager/api/remove · {"name":"<skill>"}
POST · /dsh-skill-mini-manager/api/restore · {"id":"<trash entry>"}
POST · /dsh-skill-mini-manager/api/purge · empty the trash
Installing
This is a standard DSH bundle (it declares dsh.bundle.patch and dsh.client).
Pick any one of three routes; all three need one dsh web restart afterwards.
One thing to know up front: this plugin has no build step — the browser half
lib/client.js is a hand-written `__ModuleLoader__
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::5,最后更新 2026-10-16