English | 中文
A self-contained standalone repository template for an ESM Cordis plugin. Every source file, compiler setting, test fixture, contributor instruction, skill, and build helper used by the repository is inside this directory; every development input resolves below this repository root.
Normal npm dependencies are resolved from the package registry. A DSH host is a runtime consumer of the finished package, not a source or build input.
.
├── .oxlintrc.json # Type-aware Oxlint configuration
├── .agents/skills/ # Repository-local plugin development workflow
│ ├── dsh-plugin-development/ # End-to-end coordinator
│ └── dsh-plugin-*/ # Plan, scaffold, implement, compose, test, release
├── docs/
│ └── dsh-plugin-contracts.md # Shared local contract for all plugin skills
├── patches/
│ └── README.md # Dependency and DSH-host patch contract
├── scripts/
│ ├── extract-patch.mjs # Config-driven host patch regeneration (see patches/README.md)
│ └── patch.sh # Idempotent host patch application
├── src/
│ ├── README.md # Growth rules for services and feature modules
│ ├── config.ts # Serializable schema and resolved defaults
│ ├── index.ts # Loader-facing function-plugin namespace
│ ├── invariant.ts # Package-owned invariant companion
│ └── runtime.ts # Fakeable host boundary and Cordis activation
├── tests/
│ ├── README.md # Harness, feature-test, and snapshot conventions
│ ├── harness.ts # Shared real-Cordis test mount
│ ├── plugin.spec.ts # Loader export and activation tests
│ └── snapshots/
│ └── README.md # Optional product-visible fixture contract
├── .gitignore # Generated artifact exclusions
├── AGENTS.md # Repository-local contributor rules
├── LICENSE # Template license
├── README.md # Repository and usage contract
├── cordis.patch.yml # Profile bundle contribution
├── package.json # Exports, peers, dsh.bundle.patch
├── pnpm-lock.yaml # Reproducible registry dependency graph
├── pnpm-workspace.yaml # Package-manager and optional patch policy
├── tsconfig.json # Compiler and type-aware lint project
├── tsdown.config.ts # Direct source-to-runtime/declaration build
└── vitest.config.ts # Test runner configuration
A 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, and tests in the owners appropriate to the package; the template does not require every plugin to copy one fixed directory layout.
The template's sample skeleton still uses src/index.ts, src/config.ts, src/runtime.ts, src/invariant.ts, tests/harness.ts, and tests/plugin.spec.ts; retain those owners when they fit the package, and document any deliberate replacement. Stable product-visible expected output belongs under the package's actual snapshot owner. Dependency and DSH-host patches use the optional patches/ contract when needed.
- Replace package identity in
package.json, the Loader owner, configuration/runtime/invariant owners, focused test owners, bundle metadata, TypeScript metadata,README.md, andAGENTS.mdas applicable. The sample skeleton names these owners explicitly; a deliberate replacement must update the package's local documentation and static-analysis configuration too. - Replace the template package name
@your-scope/dsh-plugin-templateand plugin ids only in those identity owners. Do not perform a global replacement inside.agents/skills/; its generic examples and marker checks must remain reusable. - Update
description,LICENSE, andcordis.patch.yml. - Add only the DSH host services used by the implementation to the package contract and composition patch. Keep source and build dependencies resolvable from this repository's
node_modules; host-provided runtime APIs remain consumer-supplied peers. - Replace the empty invariant installer when the package owns an authoritative event or mutable data relationship.
- Implement activation and host-boundary behavior in the actual runtime/service owners, moving cohesive capabilities into project-specific modules as needed. Keep
src/index.tslimited to Loader metadata and public re-exports when that matches the package, and scope registrations throughctx.effect(),ctx.on(), or registry disposers. - Keep every source, compiler, documentation, and project-reference path inside this repository. Describe files from the project root, for example
docs/dsh-plugin-contracts.md. Do not add local-pathlink:orfile:dependencies. - Set
privatetofalseonly when the package's public dependencies and distribution artifacts are ready.
Do not add a default export to a function plugin. Cordis Loader unwraps exports.default ?? exports; a stray default export discards namespace exports such as inject, Config, and apply.
DSH discovers the repository-local workflow under .agents/skills/. Start with dsh-plugin-development for the complete sequence, or invoke one stage directly:
| Skill | Purpose |
|---|---|
dsh-plugin-plan | Decide plugin form, dependencies, configuration, invariant, composition, and evidence. |
dsh-plugin-scaffold | Instantiate and baseline-verify a new repository from this template. |
dsh-plugin-implement | Implement lifecycle-safe Cordis behavior, metadata, docs, and invariants. |
dsh-plugin-compose | Install the bundle into an isolated profile and prove effective activation. |
dsh-plugin-test | Verify Loader exports, behavior, disposal, composition, snapshots, and artifacts. |
dsh-plugin-release | Check local, Git, or npm distribution readiness without publishing implicitly. |
Keep these directories when copying the template so future sessions rooted in the plugin repository retain the same workflow.
Run every command from this directory:
pnpm install
pnpm run lint
pnpm test
pnpm run buildlint runs Oxlint with type-aware analysis and denies warnings for the configured source and test projects. build runs the configured source-to-artifact pipeline, including any declaration assembly or final artifact verifier owned by the package, and emits ready-to-pack output; it does not run an install-time lifecycle build.
The release artifact is built from the configured source owners before packing. Profile or consumer installation uses the ready-made lib/ output and does not run prepare; pnpm pack --dry-run --json verifies the final archive contents.
Two GitHub Actions workflows ship with the template:
.github/workflows/ci.yml— every push tomainand every pull request: install with the frozen lockfile, Oxlint static analysis, tests, and build..github/workflows/release.yml— every push tomain: runs Oxlint, tests, builds, packs the ready-made tarball (pnpm pack), and follows the repository's configured GitHub Release policy.
The package manifest declares the bundle patch:
{
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml"
}
}
}A DSH host may install this package into a profile and apply cordis.patch.yml over its own runtime composition. That host integration is intentionally outside this repository's build and test inputs. The patch composes plugins; it does not alter host source, compiler settings, build scripts, or catalogs.
The invariant companion uses a narrow local interface for the host's invariants service. This keeps the package build independent of the host's private source package while preserving the runtime registration used by a DSH profile.
This template demonstrates a function plugin and therefore named exports:
// src/index.tsexportconstname='plugin-template'exportconstinject: string[]=[]export{Config}from'./config.ts'export{apply}from'./runtime.ts'// src/config.tsexportinterfaceConfig{/* serializable fields */}exportconstConfig: z<Config>=z.object({/* validation and defaults */})// src/runtime.tsexportfunctionapply(ctx: Context,config: Config): void{/* effects */}A service provider instead normally default-exports its Service subclass. Do not mix the two forms.
Before considering packed or GitHub Release distribution, build and inspect the final archive:
pnpm run lint
pnpm test
pnpm run build
pnpm pack --dry-run --jsonThe final package must contain every runtime and declaration file named by main, types, exports, and files. Keep private: true until the package's DSH host peers are available through the selected distribution channel.
The included test proves Loader-safe ESM exports and schema-resolved activation. Replace the activation assertions with observable behavior and disposal assertions for every registry contribution. Product-visible plugins should add a real Loader/profile composition test in the consuming DSH application rather than relying only on hand-mounted unit tests.