# Package and install a plugin English | [中文](publish.zh.md) The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `dsh plugin add`, and explains the layer order that determines the composed configuration. It assumes the `dsh` CLI is installed. Complete [plugin configuration](./config.md) first. To use a fresh source checkout instead, complete the [run-from-source section](../../../../README.md#run-from-source), keep this tutorial's `hello-plugin` directory at the repository root, and run the remaining `dsh ...` commands from there as `pnpm dsh ...`. See [source execution](../../../../apps/cli/reference/README.md#source-execution) for build and launcher behavior. ## Two concepts, two manifests Installation is built on two concepts. Both are described by a `package.json`, but they carry different kinds of manifest under the `dsh` key, and they answer different questions: - A **bundle** is an npm package that ships a configuration layer. Its manifest declares `dsh.bundle`, answering "what does this package contribute?": a patch file that inserts or overrides plugin rows. - A **profile** is a directory under `$DSH_HOME/profiles/` describing one runnable composition. Its manifest declares `dsh.profile`, answering "which bundles compose this setup, in what order?". A bundle is what you author and distribute; a profile is what a user boots with `dsh --profile `. Nothing is both. ### The bundle manifest Create the package directory: ```sh mkdir -p hello-plugin ``` ``` hello-plugin/ ├── package.json # declares dsh.bundle ├── cordis.patch.yml # the layer applied when a profile lists this bundle └── index.js # plugin modules the patch rows reference ``` Create `hello-plugin/package.json`: ```json { "name": "dsh-hello-plugin", "version": "0.1.0", "type": "module", "main": "index.js", "files": ["index.js", "cordis.patch.yml"], "dsh": { "bundle": { "patch": "./cordis.patch.yml" } } } ``` Create `hello-plugin/index.js` with the plugin entry point: ```js export const name = 'hello-plugin' export function apply() { console.log('[hello-plugin] plugin loaded!') } ``` Create `hello-plugin/cordis.patch.yml`. The patch is a YAML array like the `--patch` overlays you wrote, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code: ```yaml - insert: - id: hello name: dsh-hello-plugin ``` A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable. ### The profile manifest A profile directory holds two files: - `package.json` — the profile's out-of-tree plugin dependencies (managed by pnpm) plus the `dsh.profile` manifest with its ordered `bundles` list. - `cordis.patch.yml` — the user's own patch layer, applied after every bundle layer. You never write a profile manifest by hand: `dsh --profile --from-default-profile