This reference ships with the template so planning, implementation, testing, and distribution use only guidance stored below the repository root. It targets the DeepSeek Harness 0.2.1-rc.2 plugin contract.
All source, TypeScript configuration, test fixtures, skill instructions, and contributor guidance used by this template live below the repository root. Describe repository files with project-root paths such as docs/dsh-plugin-contracts.md; parent-directory navigation is not valid documentation. Paths that leave the repository are not valid template inputs. Ordinary npm dependencies are allowed; a dependency is not a source or configuration file reference.
A DSH host is a runtime consumer of the finished package, not a development input. The host supplies Cordis services and applies the package's bundle patch when the package is installed into a profile.
A function plugin exports name, inject, Config, and apply as one ESM namespace and has no default export. A service plugin default-exports its service class and follows the host service lifecycle. Do not combine the two loader forms.
Required Cordis services belong in inject. Optional services are read through ctx.get(name) and must tolerate absence or attach and detach through a scoped injection. Configuration is a serializable Schemastery schema from @deepseek-ai/schemastery; deployment-varying choices are fields, not hidden constants. The schema value is named Config; the public shape type is PluginConfig, keeping the value and type declarations distinct.
Package identity is explicit: an npm package name may be scoped or unscoped, and the template does not require an @scope/dsh- prefix. Keep the selected package name exact in package.json, bundle rows, exports, tests, display metadata, and documentation; the package name and Cordis plugin ID remain separate concepts.
The public manifest fields this template uses are:
engines.node: supported Node.js versions.engines.dsh: supported DSH versions as a SemVer range, including prerelease versions. When present it decides compatibility alone andpeerDependencieson@deepseek-ai/dshor@deepseek-ai/dsh-*are ignored.dsh.manifestVersion: the public manifest format, currently1.dsh.bundle.patch: one patch file path or an ordered list applied as one bundle layer.dsh.client: browser module declaration withplatform: 'web', optionalinjectpackage-name edges, optionalimmediately, and optionalexternalexact module requests.
Installation and profile startup evaluate the requirement before importing the plugin. A mismatch is refused unless the profile stores an exact-version exemption for that package version and runtime version. Prereleases participate in range matching, so use an explicit prerelease range such as >=0.2.1-rc.2 <0.3.0 when the package targets this release line.
Classify every package by who supplies it:
- Peer dependency: a host-supplied runtime API. Cordis, Schemastery, and any consumed
@deepseek-ai/dsh-*package belong here; add the same package todevDependencieswhen source, lint, tests, or the build import it locally. - Dependency: a library the plugin owns and ships with.
- Development dependency: tooling used only for lint, tests, or builds.
This template uses @deepseek-ai/cordis and @deepseek-ai/schemastery as peers and development dependencies, and @deepseek-ai/cordis-plugin-loader as a development dependency. Keep every consumed @deepseek-ai/dsh-* service as a peer with a reachable development copy. Do not use unscoped cordis, schemastery, or @cordisjs/plugin-loader in a 0.2.1 plugin. Do not use link: or file: specs, and do not bundle a host runtime.
Plugin Manager and Settings read display metadata without activating the package:
./locale/en.jsonis the discovery subpath and ownsmeta.titleandmeta.description; other language files use the same fields. The physical files may live elsewhere (this template maps the subpaths tosrc/assets/locale/), but every locale file must share the directory of the resolved English file.- Export
./locale/*.jsonand list the physical files infiles. - Set a top-level
iconinpackage.jsonor export./icon. The image may live at any contained path (this template usessrc/assets/icon.svg), must be SVG, PNG, JPEG, or WebP, at most 256 KiB, contained in the package after realpath resolution, and must not be an absolute path, URL, or escaping symlink. - Missing locale fields fall back to
package.jsonname and description; missing images use the panel default artwork. - A subpath plugin is not a package and never reads
package.json; it exports<subpath>/locale/*.jsonand<subpath>/icon.
Malformed metadata produces a diagnostic while retaining valid text. Installation and slot registration alone do not prove the rendered result; verify the intended title, description, and icon in the consuming profile.
The package may be host-only, client-only, or split across host and browser faces. Keep Loader metadata, configuration, runtime/service boundaries, browser behavior, shared contracts, tests, snapshots, and optional patch files in explicit owners appropriate to the package; do not force a fixed directory layout onto every plugin.
The sample skeleton separates src/index.ts, src/config.ts, and src/runtime.ts, with tests/plugin.test.ts and tests/harness.ts. Larger packages may replace or extend those owners with capability-named modules, focused test directories, and a package-specific snapshot owner when the planned behavior warrants it. patches/ remains optional and carries only documented dependency or DSH-host corrections. A host change that cannot be provided through official plugin registration must use that controlled host-patch process and remain outside the published package. cordis.patch.yml is not a host source patch.
Every listener, registry entry, timer, watcher, child process, and callback registered by a plugin belongs to its Cordis fiber. Use ctx.effect(), ctx.on(), or the registry's returned disposer and test removal after fiber disposal. Publish state and emit events only after the owning operation succeeds. A waterfall listener delegates by calling next().
package.json declares the bundle patch with dsh.bundle.patch. The value may be one path or an ordered list; each path is relative to the package root, and the launcher applies the list in order as one bundle layer. cordis.patch.yml inserts or overrides plugin rows; it does not change source files, compiler settings, catalogs, or launcher code. An id-targeted override replaces the complete config, so retained fields must be restated. The package row name must resolve through the consuming DSH profile.
Rows in a bundle patch use insert or id-targeted overrides. Layers apply in dsh.profile.bundles order; profile, home, command-line, and launcher patches may override them later. An id-targeted override replaces the row's complete config value, so retained fields must be restated. A patch targeting an absent id warns on stderr; inspect the effective tree and treat the warning as failed composition unless absence is intentional. The Loader dialect supports !!js expressions only in plugin config; metadata such as id, name, and disabled stays literal. Keep credentials out of patches; use credential references or environment-variable names. An empty or comments-only patch parses to no list and is invalid; use [] for an intentionally empty layer.
A plugin or bundle whose requirement does not match the running DSH is refused at installation and skipped or denied at profile startup. The refusal names the requirement key (engines.dsh or a DSH peer package) and the unmet range. Users can grant an exact package-name@version to exact runtime-version exemption through dsh plugin allow-version, dsh plugin revoke-version, or the plugin manager; the grant is stored in the profile's compatibility.json. An exemption never authorizes package build scripts.
A browser-facing package declares dsh.client and exports its built bundle at ./client. The host scans live Loader entries, composes the Web boot graph, and serves each bundle; executing a bundle only registers a lazy factory whose ID equals the package name. Keep factories side-effect free, register resources inside apply with ctx.effect/ctx.on, and externalize only modules the host supplies through the module table. Declare non-baseline runtime requests in dsh.client.external. A source import() split by the client build becomes require.async(...) and loads its package-local chunk lazily.
Client UI copy belongs to typed locale dictionaries registered through ctx.locale.register() and the framework translation seat; plugin display metadata in the exported ./locale/*.json resources (here src/assets/locale/*.json) is a separate host-facing channel. Route settings through the current ctx.configForms API after inspecting the installed settings contract; do not assume a removed settings-scope service.
The minimum package evidence includes a real Loader export-shape test, schema/default behavior, observable plugin behavior, and disposal. Product-visible behavior additionally needs a real assembled composition test in the consuming DSH application and a focused keyless snapshot when output is stable and user-visible. Type-aware static analysis, tests, and a development build are separate checks.
The development and release build is:
pnpm run lint
pnpm test
pnpm run build
pnpm pack --dry-run --jsonThe Oxlint configuration is the repository's static-analysis gate. It performs type-aware analysis and denies warnings. The release build produces a ready-made artifact; pnpm pack --dry-run --json inspects the complete file list. It emits every runtime and declaration file promised by main, types, exports, and files using only this repository's installed dependencies. Profile/plugin installation consumes that packed artifact and does not run an install-time prepare hook. A package is ready for packed or GitHub Release distribution only when every manifest-declared runtime and type entry exists after the build.
Git installs fetch sources instead of built artifacts. Only choose that channel with a self-contained prepare build and the consumer's explicit package-manager build-script approval; npm and tarball installs consume prebuilt output without lifecycle build permission.