@deepseek-ai/dsh-claude-plugin-loader
English | 中文
Standalone extraction. This repository mirrors
packages/claude-plugin/claude-plugin-loaderfrom the DeepSeek Harness monorepo under its MIT license. The@deepseek-ai/dsh-*peer dependencies are built inside that monorepo and are not published to npm yet, sonpm installhere cannot resolve them; run tests and builds from the monorepo checkout (pnpm exec vitest run packages/claude-plugin/claude-plugin-loader).docs/design-report.htmlis the Chinese design report for the implementation.
A cordis plugin that loads Claude Code plugins directly on the harness: it discovers plugin roots (explicit plugins entries plus recursively scanned roots), parses each plugin's mapped surface, and mounts every component onto its native harness seam. Everything a plugin contributes is effect-owned by the loader fiber, so disposing the loader unloads the whole plugin surface.
| Claude component | Harness seam |
|---|---|
skills/<name>/SKILL.md |
one claude-plugin skill provider from @deepseek-ai/dsh-skill over every parsed skill |
commands/<name>.md |
one dsh-commands (@deepseek-ai/dsh-commands) slash command steering a rendered prompt into the agent |
agents/<name>.md |
one dsh-tool-subagent (@deepseek-ai/dsh-tool-subagent) delegation tool per agent (needs subagentProvider) |
hooks/hooks.json |
one dsh-hooks-claude-code (@deepseek-ai/dsh-hooks-claude-code) bridge instance per plugin |
.mcp.json servers |
one dsh-mcp-client (@deepseek-ai/dsh-mcp-client) instance per server |
Config
import type { Config } from '@deepseek-ai/dsh-claude-plugin-loader'
const config: Config = {
plugins: ['/path/to/plugin'], // optional: explicit plugin roots, each required to carry .claude-plugin/plugin.json
roots: ['/path/to/marketplace'], // optional: scan roots searched up to maxScanDepth for plugin roots
maxScanDepth: 4, // optional: directory levels below each roots entry to descend (0–16)
subagentProvider: 'spawn', // optional: ctx.subagents provider that mounts plugin agents as delegation tools
mcpToolCallTimeoutMs: 60_000, // optional: per-tool-call timeout forwarded to every mounted MCP client
mcpFailOnStartupError: false, // optional: whether an MCP server startup failure fails the loader activation
}
In a cordis.yml:
- dsh-claude-plugin-loader:
plugins:
- ./.claude/plugins/my-plugin
roots:
- ./.claude/plugins
subagentProvider: spawn
Loading is fail-loud: a missing explicit root, a malformed manifest or component file, a duplicate plugin/command/agent name, or two MCP servers mapping to the same sanitized serverName all throw at load with the offending path. A config with neither plugins nor roots warns and mounts nothing. Claude-valid inputs the harness cannot serve are skipped with a warning instead — a skill's allowed-tools, an agent's model/description overrides, an sse MCP server, an agent body containing a {{…}} group (the persona seam would interpolate it as a prompt variable), agents when subagentProvider is unset, and hooks when no shell service is loaded. Skill files load with model-invocable on and user-invocable off unless the file overrides them; argument-hint becomes the candidate's whenToUse.
Commands render Claude Code's argument rules: $ARGUMENTS receives the full input, $1…$9 receive whitespace-split arguments (missing ones become the empty string), a body referencing neither appends the input, and ${CLAUDE_PLUGIN_ROOT} is replaced with the plugin's absolute root (also in MCP command/args/env/cwd/url/header values). The command description comes from frontmatter or the first non-empty body line.
Model Experience
Loaded skills
What the model sees
Each parsed skill joins the skill catalog as a candidate named by its SKILL.md name (source claude-plugin:<plugin>, rank 350), with its description, invocation flags, and optional whenToUse. Invoking a skill loads its SKILL.md body verbatim as skill instructions.
Token effect
Catalog listings carry names, descriptions, and hints only; each skill body costs tokens only while the skill is loaded, and reloads on demand.
KV Cache effect
Append-only: skill bodies enter later model requests as newly added context after the reusable prefix, without rewriting it.
Plugin commands
What the model sees
Running a plugin slash command steers the rendered command body into the session as a user-sourced message, exactly like a typed command line.
Token effect
One user message per invocation, sized by the command body plus the supplied input.
KV Cache effect
Append-only; the steered message joins the conversation after the reusable prefix.
Plugin agents
What the model sees
Each agent becomes a delegation tool named subagent_<name>; the agent's body is the subagent persona sent with every delegated child conversation.
Token effect
A one-line tool schema per agent; the persona text is resent as part of each delegated child request.
KV Cache effect
Persona text is child-session context and does not touch the parent request prefix.
Known Limitations and Deferred Work
allowed-toolsis ignored: the harness has no per-skill tool restriction, so restricted skills load unrestricted (warned). Tool control is available through the guard and permission planes instead.- Agent
description/modeloverrides are not surfaced: the subagent seam owns its own description and model policy, so overrides are warned about and dropped. sseMCP transport is unsupported: onlystdioandhttp(Streamable HTTP) servers load; ansseserver is skipped with a warning.- Agent bodies containing a
{{…}}group are skipped: the subagent persona seam interpolates{{name}}references as prompt variables and fails at child render, so such agents are skipped at mount with a warning; reword the braces to load the agent. - Hooks need a loaded shell service and run only the hook subset that
dsh-hooks-claude-code(@deepseek-ai/dsh-hooks-claude-code) maps; withoutctx.shellthe plugin's hooks are skipped. - One MCP client per server with no tool remapping; a startup failure is contained unless
mcpFailOnStartupErroris set. - Local plugin directories only: the loader mounts plugins already on disk; downloading or versioning marketplace plugins is out of scope. To consume a Claude marketplace, clone its entries into a cache directory, then point
rootsat the cache (or list chosen entries inpluginswhen siblings collide on command names). Scans skip hidden directories,node_modules, and symlinks, and stop at a plugin root.
No comments yet. Be the first to write one.