插件目录 / Developer / @wulongxi/dsh-skills-hub
@wulongxi/dsh-skills-hub
已验证 · 实测可装 WLX2007
功能简介
DeepSeek Harness (DSH) 技能中心插件 —— 原生 Web GUI 面板:搜索网络技能市场(SkillHub / skills.sh / GitHub)、一键安装到用户/项目根、管理与创建本地技能。npm: @wulongxi/dsh-skills-hub
可用 — 实测通过,早期项目
DeepSeek Harness (DSH) 技能中心插件 —— 原生 Web GUI 面板:搜索网络技能市场(SkillHub / skills.sh / GitHub)、一键安装到用户/项目根、管理与创建本地技能。npm: @wulongxi/dsh-skills-hub 实测能干净安装、正常启动。早期项目,但功能可用。
「已验证」表示我们的自动化 CI 在干净 profile 里实际执行了 dsh plugin add 并启动成功——仅此而已。功能描述与版本兼容性均为作者声明。这不是安全审计,也不代表对第三方代码的背书。
README
@wulongxi/dsh-skills-hub
Skills Hub is a DeepSeek Harness (DSH) plugin that adds a native, first-class panel to the web GUI for discovering, installing, and managing Agent Skills.
It ships three capabilities in one panel:
- Market — search online skill registries (default: skillhub.cn, plus skills.sh and any public GitHub repository), preview a skill's full documentation, and read its file manifest before deciding to install.
- My Skills — browse every skill the runtime has loaded, grouped by source; enable or disable skills, edit their
SKILL.md, or remove them (deletes go to.trash, never straight to disk loss). - Create — write a new skill into the user-level or project-level skill root, with frontmatter handling handled for you.
The panel reads the official skill roots (<project>/.dsh/skills, <project>/.agents/skills, ~/.dsh/skills, ~/.agents/skills, plus any configured customSkillDirs) and merges the host's skill registry on top, so what the panel displays is exactly what the model receives.
| Status | Market search & preview: available. Skill installation: planned (see Roadmap). |
| Requires | DSH >= 0.2.0-rc.2 |
| License | BSD-3-Clause |
| Languages | English, 简体中文 (follows the GUI language setting) |
Installation
From a local checkout (link install)
dsh plugin --profile <profile> add link:<path-to-this-repo>
Restart the DSH service. The sidebar then shows a Skills Hub entry; selecting it opens the panel in the center column.
From npm (once published)
dsh plugin --profile <profile> add @wulongxi/dsh-skills-hub
Usage
Market tab
Type a query and press Enter. The source picker to the left of the search field selects which registry the search asks; the choice applies to the next search, so results always match the query that produced them.
- skillhub.cn (default) — a China-hosted skill store with 190,000+ skills, reachable directly with no network accelerator. The site explicitly invites agent integration: it publishes an agent-facing integration document (
install/skillhub.md), allows crawlers inrobots.txt, and its read path is unauthenticated. Rows carry Chinese descriptions, download counts, and icons. - skills.sh — the Agent Skills registry's search endpoint. This source serves one page; the panel says so honestly instead of offering a load-more control that could only return empty.
- GitHub repositories — a repository can also be browsed directly: any
owner/repo(a full URL or a.gitsuffix also works) is enumerated through the GitHub API, and every skill directory inside it becomes a row.
Long result lists load the next page automatically as you scroll, with an explicit "load more" button as the keyboard/click target. Selecting a skill's name opens a detail dialog rendered by the shell's own markdown renderer — the skill reads exactly as it does in a conversation — alongside its file list and total size.
Installing from the market
The detail dialog carries the install action. A skill is installed after its body has been read, so the licence line and the file list sit on the same screen as the button. Installing asks twice: the target root (user ~/.dsh/skills, shared by every project, or the current project's .dsh/skills), then a confirmation that states the licence situation at the moment of decision.
The install itself is engineered to fail cleanly:
- Staged then renamed. Files land in a staging directory beside the target and the directory is renamed into place only when every file arrived. A failed or cancelled install leaves the previous state of the disk untouched — no half-written skill the model could later load.
- Collision is an error. An existing skill with the same name is never silently replaced (the panel answers 409; updating an installed skill is M4's job).
- Bounded. Per-file 2 MB, 16 MB total, 200 files, and every manifest path is validated against a strict relative-path grammar before anything is written.
- Provenance travels with the skill. A
.skills-hub.jsonrecord inside the installed directory names the market id, the source, the licence at install time and the timestamp — the anchor M4's update checks read back.
Licences are stated, never laundered. Every result row declares its licence: declared ones show it (MIT, GPL-3.0, …); undeclared ones show No licence declared. In a sampled catalogue more than half of the entries declare none, and that is a fact a user needs before deciding to copy a skill to disk.
My Skills tab
Skills are grouped by provenance (project, user, plugin providers, runtime registration). A skill backed by a file can be enabled or disabled — the toggle rewrites disable-model-invocation in its frontmatter — edited, or deleted. Skills registered by plugins at runtime have no file on disk; the panel says so once, per group, instead of decorating every row, and their enable state is stored in the panel's own configuration.
Preview
Every skill can be previewed, including bundled and runtime-registered ones that have no SKILL.md on disk — the host falls back to the runtime skill registry for those, and the dialog labels the source honestly ("from file" / "from skill registry"). Frontmatter fields are shown as a definition list above the body and are never mixed into the markdown, because a leading --- fence is a thematic break in GFM and would mangle the YAML into headings and stray prose.
Configuration
Plugin config (profile patch):
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Master switch; false mounts no routes. |
githubProxies |
string[] |
built-in chain | Accelerator prefixes for GitHub raw content, tried in order; include "" to try direct first. |
customSkillDirs |
string[] |
[] |
Additional skill root directories to scan. |
dshHome |
string |
$DSH_HOME or ~/.dsh |
Override for the user config root. |
Security model
- Every route sits behind the shared loopback fence (socket address +
Hostheader + browser same-origin markers). An unpaired LAN client receives403 forbidden: loopback-onlybefore any skill file is touched. Whendsh-remote-web-uiis loaded, a live paired-device cookie is an additional allow path; unpaired and revoked devices still receive 403. - A client-submitted
pathis only an identity claim. Before touching any file, the host re-scans and requires the same skill name to resolve to that exact path. A project skill that disappeared can therefore never be redirected onto a same-named user skill, and the routes cannot be used to read or write an arbitrary path. - The browser never sends a URL. Market requests carry only the opaque skill id the host issued; repositories, refs, directories and file paths are re-derived host-side, and the id grammar is validated segment by segment, so a path traversal is rejected before it can reach any fetch.
- Remote fetching happens only in the host half, behind the trust fence. The unauthenticated GitHub API allows 60 requests/hour, so only discovery (tree listing) touches it, cached for 30 minutes; file bodies come from
raw.githubusercontent.comthrough an accelerator chain, which does not count against that budget. skillhub.cn is reached directly. - Registry icons are proxied, not linked. Icon URLs are fetched host-side under an explicit policy (https only, allow-listed host, image content-type, 256 KB cap) and re-served as data URLs, so the browser never loads a third-party origin: no referrer leak, no tracking pixel per render.
- Installs re-derive everything from the id. The install route receives only the opaque skill id and the workspace the panel is showing; the file list comes from the skill's own detail manifest (never client-supplied), the target directory is derived host-side, and every manifest path is validated before it is joined onto the staging root. An install can therefore only write a skill directory under a skill root.
- Writes are bounded (64 KB bodies), symlinks refuse delete/update (both would escape their skill root), and deletes move files into
.trashrather than removing them.
Architecture
The package is a dual-half DSH plugin:
- Host half (
lib/index.js, runs in the DSH server process) — serves the/api/skills-hub/*route family, owns all network access, file I/O, and the trust fence. - Client half (
lib/client.js, runs in the web GUI) — registers the sidebar entry and the center-column page through the shell's official slot seats (sidebar.panellistand the keyedmainslot), so the shell owns the row box, active highlight and panel switching exactly as it does for its own Plugins and Schedule pages.
The client is built only on the host's frozen browser module table (react, @deepseek-ai/cordis, @deepseek-ai/dsh-client-ui-*); every control is the official shared primitive (Button, Switch, Input, Menu, Modal, MarkdownText, …) — nothing is hand-drawn. Registration failures are logged and degrade the panel; they never fail the GUI's boot.
Development
pnpm install
pnpm typecheck # tsc --noEmit
pnpm build # tsc -b tsconfig.build.json && tsdown
Test suites (run with node tests/<file>.mjs):
| Suite | Scope |
|---|---|
m0-smoke.mjs |
Loads both bundles the way the host does; asserts the loader contract, injected-require usage and route registration. |
m1-host.mjs |
Drives the route family against a real temporary skill tree: scan grading, write-route identity checks, symlink protection, fence denials. |
market.mjs |
Market layer with fake transports, zero network: aggregation, dedup, id grammar (incl. path-escape attempts), source contracts, installer staging/path-safety/provenance. |
ui-structure.mjs |
UI guards: official-primitives-only, card anatomy, scroll height chain — each negative-controlled. |
theme-tokens.mjs |
Verifies every referenced CSS custom property exists in the shipped theme, because an undefined token makes var() silently drop the whole declaration. |
market-live*.mjs, icon-live.mjs, install-live.mjs, tools/scroll-selftest.mjs |
Manual, live-network / real-browser smoke tests; not part of the automated gate. |
Project layout
src/
index.ts host entry: plugin contract, config, mount-once guard
routes.ts /api/skills-hub/* handlers
access.ts, loopback.ts,
pair-access.ts trust fence (loopback + paired-device cookie)
http.ts bounded body reader, JSON writer
core/ multi-root skill scan, frontmatter parse/rewrite,
provider toggle store
market/ source registry: fetch limits, TTL caches, accelerator
chain, skill-id codec, source adapters
client/ browser half: dictionaries, slot registrations, panel
tests/ automated suites (see Development)
docs/ design documents (Chinese)
Roadmap
| Milestone | Scope | Status |
|---|---|---|
| M0 | Plugin skeleton, sidebar entry, build pipeline | ✅ Shipped |
| M1 | Local management: browse / enable / edit / create / delete / preview | ✅ Shipped |
| M2 | Market: multi-source search, detail preview, paging, icons | ✅ Shipped |
| M3 | Installation engine: validation, size caps, atomic writes, provenance | ✅ Shipped |
| M4 | Lifecycle: provenance store, update checks, local-modification detection | Planned |
| M5 | Custom registry manifests, offline import, accelerator settings UI | Planned |
License
BSD-3-Clause © WLX2007
安装
装一次目录插件,之后本站所有插件都能让 DeepSeek Harness 自动找、自动装:
dsh plugin add dshbase-catalog 然后对 agent 说「帮我装 @wulongxi/dsh-skills-hub」,它会在目录里找到并自动安装。文档:dshbase-catalog · 已验证场景包。
Web profile:
dsh plugin --profile web add @wulongxi/dsh-skills-hub Headless(CLI)profile:
dsh plugin --profile headless add @wulongxi/dsh-skills-hub 包信息
npm:@wulongxi/dsh-skills-hub · 版本 0.1.1 · 实测环境 dsh 0.1.0-rc.6
实测报告
端到端验证通过:dsh 0.1.0-rc.6 上 L1 安装 + L2 加载 + L3 运行问答。