Files
deepseek-harness/.agents/notes/implemented/feature/2026-08-15-skill-manager.md
T
Pine a53769955b feat(plugin): plugin marketplace, durable install persistence, skill manager
Add an in-app plugin marketplace fed by a remote web catalog: the host
plugin-inventory gateway gains marketplaceList/Install/Uninstall remotes,
a durable per-user install table reconciled against the actual profile, and
git/npm/tarball/bundle install paths with non-interactive git and vendored
pnpm. The Web Settings surface gains a sibling 插件市场 tab that lists the
catalog with recommended badges, repository links, and a combined sort
(recommended first, then catalog priority, then id).

Add a host + client skill manager: list local skills by direct filesystem
discovery, and install/uninstall/toggle/edit their SKILL.md from git/npm/
tarball/local sources in the writable user root.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 17:04:36 +08:00

3.9 KiB

Agent Note: skills manager settings tab + built-in host plugin

Status: implemented

English | 中文

Problem

Skills are discovered by dsh-skill-filesystem from filesystem roots ($DSH_HOME/skills, project .agents/skills, bundled, …) as SKILL.md bundles, but there is no management surface — no way to see all local skills, install one from a git/npm/tarball/local source, uninstall it, or toggle its invocation. The plugin settings section had config/list/marketplace tabs but no skills tab.

Decision

Add a skills management tab beside 插件配置 / 插件列表 / 插件市场 in the Plugins settings section, backed by a new built-in host plugin.

Host: packages/host/skill-manager (@deepseek-ai/dsh-host-skill-manager). SkillManagerGateway (serviceKey skillManager) exposes five direct Remotes:

  • list() — reads ctx.skills.list({}) (the global layer: user/bundled/custom/runtime skills) and marks managed = lives in $DSH_HOME/skills (source === 'user-dsh').
  • install(spec) — resolves a git | npm | tarball | local source into a validated SKILL.md bundle and copies it to $DSH_HOME/skills/<name>; the provider watcher discovers it with no restart.
  • uninstall(name) — deletes a user-root skill; refuses names not installed there.
  • setEnabled(name, patch) — rewrites SKILL.md frontmatter disable-model-invocation / user-invocable.
  • setDescription(name, description) — rewrites SKILL.md frontmatter description.

src/install.ts materializes the source (git clone, npm pack+tar, tarball fetch+tar, local dir), unwraps a single top-level dir, locates SKILL.md (root or skills/<name>/), and validates the provider-mandated name + description. Git runs with the same non-interactive environment as the plugin marketplace (GIT_TERMINAL_PROMPT=0 + StrictHostKeyChecking=accept-new) so a first-time clone never hangs.

src/skill-io.ts parses/rewrites the frontmatter (matching the provider's key semantics) and does the read/install/delete filesystem work. No durable install table is needed: the filesystem is the installed state, unlike plugin installs which record a profile dependency.

Client: packages/client/ui-settings-skill-manager (@deepseek-ai/dsh-client-ui-settings-skill-manager). Registers settings.plugins.tab id skills (order 30). SkillManagerSettingsTab lists skills with their invocation tags and manage/read-only labels, plus an install row (source select + spec) and per-managed-skill toggle / edit-description / delete actions.

Wiring: the host + client packages are composed in packages/bundle/web-app (cordis.patch.yml + package.json), @deepseek-ai/dsh-host-skill-manager is added to REQUIRED_PLUGINS (protected, not toggleable), and the skillManager namespace is mounted in packages/api/remotes/src/client/index.ts.

Verification

  • Host: tests/skill-manager.spec.ts (Remote surface + list/install/uninstall/toggle/ description against a temp $DSH_HOME), tests/install.spec.ts (local source resolution + validation), tests/skill-io.spec.ts (frontmatter parse/rewrite + fs).
  • Client: tests/skill-manager.client.spec.tsx (render, install, toggle, edit, delete, load-failure retry).
  • pnpm run build:lib:host + pnpm run build:lib:client (regenerates the skillManager Typert Host/Client Remote artifacts); all modified packages typecheck.

Alternatives

  • Fold skill management into plugin-inventory. Rejected: skills are filesystem artifacts with a different install target than plugins (skill root vs profile deps), so a separate host package keeps each seam's ownership clear.
  • A durable install table like the plugin marketplace. Rejected: a skill is installed iff its bundle exists in the user root; the watcher already derives the catalog from the filesystem, so an extra table would only drift.